Страница 7 из 13 Первая ... 56789 ... Последняя
Показано с 121 по 140 из 259

Тема: PGA2310/11, как её готовить и как её есть :)

  1. #1 Показать/скрыть первое сообщение.
    Частый гость
    Автор темы

    Регистрация
    15.04.2009
    Возраст
    35
    Сообщений
    202

    По умолчанию PGA2310/11, как её готовить и как её есть :)

    Добрый день.Есть много вариантов как, помогите выбрать.Например
    Миниатюры Миниатюры Нажмите на изображение для увеличения. 

Название:	pga2310_preamp.jpg 
Просмотров:	5703 
Размер:	146.2 Кб 
ID:	73364  
    Вложения Вложения
    Последний раз редактировалось zindi; 14.01.2010 в 04:16.

  2. #121
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Насчет резкого повышения громкости понятно в принципе. После нуля Дб начинается усиление Уже обсуждали, что исправляется программно...

  3. #122
    Завсегдатай Аватар для Grigori G.
    Регистрация
    13.05.2006
    Адрес
    Estonia
    Возраст
    46
    Сообщений
    1,644

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)


    Offтопик:
    denz, в данном случае имелось в виду положить исходники, а не хекс
    нет ничего более вечного, чем то, что замотано синей изолентой...

  4. #123
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Grigori G., к сожалению, нет исходника

  5. #124
    Завсегдатай Аватар для Grigori G.
    Регистрация
    13.05.2006
    Адрес
    Estonia
    Возраст
    46
    Сообщений
    1,644

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Цитата Сообщение от denz Посмотреть сообщение
    После нуля Дб начинается усиление
    Ну и что же тут исправлять? Если не нужно усиление, то повышаем до 0дБ и останавливаемся. Это ж не баг, это одна из дополнительных возможностей чипа.
    нет ничего более вечного, чем то, что замотано синей изолентой...

  6. #125
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Еще про звучание. Может только мне это показалось, но оно становится тускнее, слегка мягче. И шум в паузах слегка ощутим, чего не скажешь при отсутствии предваря.

  7. #126
    iN GOD We TRUSt Аватар для EDDiE
    Регистрация
    13.02.2006
    Адрес
    GAZ-A-LAGO
    Сообщений
    13,217

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Цитата Сообщение от denz
    Еще про звучание. оно становится тускнее, слегка мягче. И шум в паузах слегка ощутим, чего не скажешь при отсутствии предваря.
    Многие из ОУ от Ti серии ОРА имеют "тухлый" звук, но шумят мало.

    Может сконфигурировано что не так или в разводке проблема.

    В EPE Mag должна быть разводка п/п.

  8. #127
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Цитата Сообщение от EDDiE Посмотреть сообщение
    В EPE Mag должна быть разводка п/п.
    Мне не понравилась их идея с односторонней платой.

  9. #128
    Не хочу! Аватар для Alex
    Регистрация
    20.03.2003
    Адрес
    Worldwide
    Возраст
    62
    Сообщений
    38,964

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Цитата Сообщение от denz Посмотреть сообщение
    Насчет резкого повышения громкости понятно в принципе. После нуля Дб начинается усиление
    А мне - не понятно. Ну начинается усиление, ну и что? Чем изменение уровня с 0дб до +6дб отличается от изменения уровня с -12дб до -6дб, или от -6дб до 0дб (когда никакого усиления еще нет) ?
    "Плавность" изменения громкости от этого не должна зависеть.
    "Замполит, чайку?"(с)"Охота за Красным Октябрем".
    "Да мне-то что, меняйтесь!"(с)анек.
    <-- http://altor1.narod.ru --> Вопросы - в личку, е-мейл, скайп.

  10. #129
    Частый гость
    Автор темы

    Регистрация
    15.04.2009
    Возраст
    35
    Сообщений
    202

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Цитата Сообщение от denz Посмотреть сообщение
    Еще про звучание. Может только мне это показалось, но оно становится тускнее, слегка мягче. И шум в паузах слегка ощутим, чего не скажешь при отсутствии предваря.
    в схеме которая выше с двумя буфферами такого не замеченно

  11. #130
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Прогоняю сигнал по тракту. Всего 85 шагов по 1,5Дб. От 00 до 35 сигнала на выходе почти не наблюдаю, при входном около 2В. После 40 начинает круто нарастать сигнал вплоть до ограничения.

  12. #131
    Завсегдатай Аватар для igorkuz
    Регистрация
    11.06.2005
    Адрес
    Москва
    Возраст
    60
    Сообщений
    2,058

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Такое попробовать не хотите?
    http://www.dh3ben.de/html/electronic...1_preamp.shtml
    Повторяемость 100%
    Кузнецов Игорь

  13. #132
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Опять же, может микросхема вышла из строя, ведь в плече +15В выскакивало 17В?

    ---------- Добавлено в 22:08 ---------- Предыдущее сообщение в 22:07 ----------

    Цитата Сообщение от igorkuz Посмотреть сообщение
    Такое попробовать не хотите?
    Не против, но платы уже есть. Тем более, схема журнальная, должно работать прилично.

    ---------- Добавлено в 22:12 ---------- Предыдущее сообщение в 22:08 ----------

    На цифровых входах +5В, при этом, на некоторых наблюдаются пачки импульсов, когда идет команда. Правильно?

  14. #133
    Частый гость
    Автор темы

    Регистрация
    15.04.2009
    Возраст
    35
    Сообщений
    202

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    а где конкретнно схема?

  15. #134
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Вид платы сверху. Так выполнены полигоны.
    Вложения Вложения
    • Тип файла: pdf PCB.pdf (139.5 Кб, Просмотров: 382)

  16. #135
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Цитата Сообщение от zindi Посмотреть сообщение
    а где конкретнно схема?

    Offтопик:
    на шестой странице

  17. #136
    Частый гость
    Автор темы

    Регистрация
    15.04.2009
    Возраст
    35
    Сообщений
    202

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Код:
    ;**************************************************************************
    ;**************************************************************************
    ;*
    ;*                  Stereo Audio Volume Control Firmware
    ;* 
    ;*                          Version 1.0  16/12/06
    ;*                           
    ;*                          Created by P.B.Smith
    ;*                  (C)2006 Silicon Chip Publications P/L
    ;*                          All Rights Reserved.
    ;*
    ;*                         www.siliconchip.com.au
    ;*
    ;*            +-----------------------------------------------+
    ;*            | This source code is NOT in the public domain! |
    ;*            +-----------------------------------------------+
    ;*
    ;************************************^*************************************
    ;*
    ;* CHANGE RECORD
    ;*
    ;* Nil.
    ;*
    ;**************************************************************************
    ;*
    ;* NOTES
    ;*
    ;* 1. Requires AVRASM v1.74 or later (set tab size to 8 in editor).
    ;* 2. Implements a software SPI port rather than using the hardware
    ;*    port to circumvent a physical layout issue.
    ;* 3. Support for up to 6 daisy-chained PGA2310*s is included but has
    ;*    not been fully implemented or tested in this version.
    ;*
    ;**************************************************************************
    
    
    .equ	VERSION_MAJOR = 1
    .equ	VERSION_MINOR = 0
    
    .equ	TRUE =  1
    .equ	FALSE = 0
    
    .equ	debug = FALSE		;for special debugging hardware only!
    
    .nolist
    .if debug
    .include "8515ndef.inc"		;AVR definitions
    .else
    .include "m8515ndef.inc"
    .endif
    
    .list
    
    ;**************************************************************************
    ;*
    ;* Registers
    ;*
    ;**************************************************************************
    
    .def	status		=R0	;state save (used by tmr0_irq)
    .def	ir_state	=R1	;ir receive state
    .def	ir_bit_cnt	=R2	;ir bit count
    .def	ir_sys		=R3	;ir received system bits
    .def	ir_cmd		=R4	;ir received command bits
    .def	ir_code		=R5	;last received command bits
    .def	irate_tmr	=R6	;interrupt rate timer
    .def	idle_tmr_lo	=R7	;idle timer (ms)
    .def	idle_tmr_hi	=R8
    .def	fast_tmr_lo	=R9	;fast adjust timer (ms)
    .def	fast_tmr_hi	=R10
    .def	sw_timer	=R11	;switch filter timer
    .def	d_timer		=R12	;display *flash* timer
    .def	spi_state	=R13	;spi state
    .def	spi_pipe_lo	=R14	;spi bit pipe
    .def	spi_pipe_hi	=R15	;
    
    .def	A		=R16	;scratch
    .def	B		=R17	;scratch
    .def	C		=R18	;scratch
    .def	D		=R19	;scratch
    .def	aflags		=R20	;various flags (see bit vars below)
    .def	bflags		=R21	;ditto
    .def	cflags		=R22	;ditto
    .def	spi_cnt		=R23	;spi word counter 
    .def	spi_bits	=R24	;spi bit count
    .def	ir_timer	=R25	;ir bit timer
    .def	XL		=R26	;scratch pointer
    .def	XH		=R27	;
    .def	YL		=R28	;spi buffer pointer (used by spi_io)
    .def	YH		=R29	;
    .def	ZL		=R30	;scratch pointer
    .def	ZH		=R31	;
    
    ; Bit variables
    
    ; aflags
    
    .equ	ir_event	=0	;infrared data available
    .equ	ir_rpt		=1	;repeat infrared code
    .equ	rot_event	=2	;rotary encoder event
    .equ	rot_dir		=3	;1=right, 0=left
    .equ	rot_f1		=4	;rotary handler flag
    .equ	rot_f2		=5	;ditto
    .equ	spi_req		=6	;spi transfer request
    .equ	wd_res		=7	;watchdog keep-alive
    
    ; bflags
    
    .equ	sw_event	=0	;switch event occurred
    .equ	sw_stat		=1	;1=switch closed, 0=open
    .equ	sw_run		=2	;switch filter active
    .equ	d_flash		=3	;flash display request
    .equ	d_state		=4	;blank display request
    .equ	d_blank		=5	;display blanked/not blanked
    
    ; cflags
    
    .equ	b_mode		=0	;balance mode
    .equ	c_mode		=1	;channel mode
    .equ	c_slave		=2	;channels slaved (future upgrade)
    .equ	v_mute		=3	;mute status
    .equ	fast_run	=4	;fast adjust timer active
    .equ	fast_adj	=5	;fast adjust mode 	
    .equ	dirty_bit	=7	;volume save pending
    
    ; vflags (stored with volume data)
    
    .equ	lr_offset	=0	;0=vol offset is left, 1=vol offset is right
    
    
    ;**************************************************************************
    ;*
    ;* Hardware specific
    ;*
    ;**************************************************************************
    
    ; Port B bits
    
    .equ	jp1		=PB0	;setup jumper
    .equ	jp2		=PB3	;0.5dB/1.5dB vol adjust jumper
    .equ	pga_mute	=PB4	;mute control
    .equ	sdi		=PB5	;SPI bus serial input
    .equ	sdo		=PB6	;SPI bus serial output
    .equ	sck		=PB7	;SPI bus clock
    
    ; Port D bits
    
    .equ	pga_cs		=PD0	;SPI bus chip select
    .equ	pga_zcen	=PD1	;zero crossing switching enable
    .equ	rot_a		=PD2	;rotary encoder *A* terminal
    .equ	rot_b		=PD3	;rotary encoder *B* terminal
    .if debug
    .equ	ir_rxd		=PD4	;ir module input
    .equ	ch_sw		=PD5	;channel switch
    .equ	bal_sw		=PD6	;balance switch
    .endif
    .equ	ack_led		=PD7	;infrared acknowledge led
    
    .if !debug
    ; Port E bits
    
    .equ	ir_rxd		=PE0	;ir module input
    .equ	ch_sw		=PE1	;balance switch
    .equ	bal_sw		=PE2	;channel switch	
    .endif
    
    ;**************************************************************************
    ;*
    ;* Constants
    ;*
    ;**************************************************************************
    
    .equ	KEY_RPT		=114	;time between ir code repeats (ms)
    
    ; Supported infrared equipment addresses
    
    .equ	TV_ADDR		=0
    .equ	SAT1_ADDR	=8
    .equ	SAT2_ADDR	=10
    .equ	CD_ADDR		=20
    
    ; Supported infrared remote key codes
    
    .equ	CH_1		=1
    .equ	CH_2		=2
    .equ	CH_3		=3
    .equ	CH_4		=4		
    .equ	CH_5		=5
    .equ	CH_6		=6
    .equ	VOL_UP		=16
    .equ	VOL_DN		=17
    .equ	CH_UP		=32
    .equ	CH_DN		=33
    .equ	MUTE_TV1	=13	;*punch-through* mute (sys = 0)
    .equ	MUTE_ALT1	=18	;alt mute: 12 (Aifa), TV1 code 191
    .equ	MUTE_ALT2	=43	;alt mute: 12 (Aifa), CD code 651
    
    ; Codes used by main function handlers
    
    .equ	chup		=1	;channel up
    .equ	chdn		=2	;channel down
    .equ	chset		=3	;channel set
    .equ	panl		=4	;pan left
    .equ	panr		=5	;pan right
    
    
    ;**************************************************************************
    ;*
    ;* Interrupt vectors
    ;*
    ;**************************************************************************
    
    	.cseg
    	.org	$0
    
    	rjmp	reset		;reset
    	rjmp	bad_irq		;INT0
    	rjmp	bad_irq		;INT1
    	rjmp	bad_irq		;timer 1 capture
    	rjmp    bad_irq		;timer 1 compare A
    	rjmp	bad_irq		;timer 1 compare B
    	rjmp	bad_irq 	;timer 1 overflow
    	rjmp	tmr0_irq	;timer 0 overflow
    	rjmp	bad_irq		;SPI transfer complete
    	rjmp	bad_irq		;UART receive complete
    	rjmp	bad_irq		;UART data register empty
    	rjmp	bad_irq		;UART transmit complete
    	rjmp	bad_irq		;analog comparator
    	rjmp	bad_irq		;INT2
    	rjmp	bad_irq		;timer 0 compare
    	rjmp	bad_irq		;EEPROM ready
    	rjmp	bad_irq		;SPM ready
    bad_irq:
    	reti
    
    
    ;**************************************************************************
    ;*
    ;* Reset entry point
    ;*
    ;* Initialise 8515 I/O ports & registers
    ;*
    ;**************************************************************************
    	
    reset:	cli
    	ldi	A,0b11111111
    	out	DDRA,A			;port A: 7 - 0 out (7-seg display)
    	ldi	A,0b00000000		;all segments off
    	out	PORTA,A
    
    	ldi	A,0b00000000		;port B: 7-5 in (SPI), 4 out (mute)
    	out	DDRB,A			;3-0 in (jumpers, unused)
    	ldi	A,0b11101111		;inputs pulled up, mute active
    	out	PORTB,A
    
    	ldi	A,0b11111111
    	out	DDRC,A			;port C: 7 - 0 out (7-seg display)
    	ldi	A,0b00000000		;all segments off
    	out	PORTC,A
    
    	ldi	A,0b10000011		;port D: 7 out (LED), 6-4 in (unused)
    	out	DDRD,A			;3-2 in (rotary), 1-0 out (pga control) 
    	ldi	A,0b01111111		;LED off, inputs pulled up, ZCEN & -CS high
    	out	PORTD,A
    
    .if !debug
    	ldi	A,0b00000000		;port E: 2-0 in (ir & switches)
    	out	DDRE,A
    	ldi	A,0b00000111		;inputs pulled up
    	out	PORTE,A
    .endif
    	sbi	ACSR,ACD		;power down comparator
    
    
    ;**************************************************************************
    ;*
    ;* Warm reset entry point
    ;*
    ;* Initialise counter/timer 0, watchdog & various flags
    ;*
    ;**************************************************************************
    
    warm_reset:
    	ldi	A,low(RAMEND)		;set stack top
    	out	SPL,A
    	ldi	A,high(RAMEND)
    	out	SPH,A
    
    ; Start timer 0
    
    	ldi	A,1<<CS00		;CT0 clocked at CK (no prescale)
    	out	TCCR0,A
    	ldi	A,128			;timer 0 up count
    	out	TCNT0,A
    	ldi	A,1<<TOIE0		;enable CT0 overflow interrupt
    	out	TIMSK,A
    
    ; Init variables
    
    	clr	A
    	mov	aflags,A		;and program flags...
    	mov	bflags,A
    	mov	cflags,A
    	mov	spi_state,A
    	mov	ir_state,A
    	sei				;enable interrupts
    
    
    ;**************************************************************************
    ;*
    ;* Initialisation
    ;*
    ;**************************************************************************
    
    ; Mute all channels
    
    init:	ldi	A,6
    	sts	pga_cnt,A		;assume 6 x PGAs on the bus...
    	rcall	mute_all		;mute *em all
    	rcall	display_clear		;blank the display
    	
    ; Init eeprom if first power up
    
    	ldi	ZL,low(ee_magic)
    	ldi	ZH,high(ee_magic)
    	rcall	ee_read
    	cpi	A,$5A			;magic number?
    	breq	in2			;continue if found
    	rcall	init_eeprom		;else initialise
    
    in2:	sbic	PINB,JP1		;skip if *setup* jumper in
    	rjmp	in5			;else continue init
    
    
    ;**************************************************************************
    ;*
    ;* Infrared setup (JP1 installed)
    ;*
    ;**************************************************************************
    
    in3:	ldi	B,5
    	rcall	flash_it		;flash *ack* LED to indicate setup
    
    ; Wait for two consecutive identical infrared codes...
    
    in4:	rcall	wait_ir			;wait for an infrared code
    	mov	C,ir_code		;save it
    	andi	C,$7F
    	mov	D,ir_sys
    
    	rcall	wait_ir			;get second code
    	mov	A,ir_code
    	andi	A,$7F
    	cp	A,C			;same code?
    	brne	in4
    	cp	D,ir_sys		;same sys address?
    	brne	in4
    
    	ldi	ZL,low(ee_blank)
    	ldi	ZH,high(ee_blank)
    	rcall	ee_write		;key code becomes blanking selector
    
    	adiw	ZH:ZL,1			;to ee_ir_sys
    	mov	A,ir_sys
    	rcall	ee_write		;save as our address
    	rjmp	in3			;loop forever
    
    
    ;**************************************************************************
    ;*
    ;* Initialisation (continued)
    ;*
    ;**************************************************************************
    
    ; Display firmware version number if *balance* switch is pressed
    
    .if !debug
    in5:	in	A,PINE			;get switch state
    .else
    in5:	in	A,PIND			;get switch state
    .endif
    	andi	A,1<<bal_sw		;keep balance switch bit
    	ldi	A,(VERSION_MAJOR*10)+VERSION_MINOR
    	breq	in6			;go display version # if pressed
    
    ; Determine number of connected PGAs
    
    	rcall	get_pga
    	tst	C			;zero detected?
    	brne	in8			;continue if not
    
    ; No PGAs found, display error code
    
    	cbr	cflags,1<<v_mute	;turn off mute indication...
    	cbi	PORTA,PA5
    
    	ldi	A,90			;error code [90]
    in6:	rcall	display			;display it
    	sbr	bflags,1<<d_flash	;enable flash
    in7:	rjmp	in7			;wait for power down
    
    ; Enable watchdog
    
    in8:	wdr
    	ldi	A,1<<WDTOE|1<<WDE
    	out	WDTCR,A
    	ldi	A,1<<WDE|1<<WDP2|1<<WDP1 ;1s (typ) timeout
    	out	WDTCR,A
    
    ; Restore volume & channel settings from EEPROM
    
    	rcall	restore_vol		;restore vol/ch settings
    	rcall	unmute_all		;unmute & display volume
    
    
    ;**************************************************************************
    ;*
    ;* Foreground loop
    ;*
    ;**************************************************************************
    
    main:	clr	idle_tmr_lo		;zero idle timer
    	clr	idle_tmr_hi
    	
    mn1:	sbrc	aflags,rot_event	;rotary encoder operated
    	rjmp	rotary_event
    
    	sbrc	aflags,ir_event		;infrared code received
    	rjmp	infra_event
    
    	sbrc	bflags,sw_event		;switch pressed/released
    	rjmp	switch_event
    
    
    ;**************************************************************************
    ;*
    ;* Idle timeout functions
    ;*
    ;**************************************************************************
    
    ; Disable fast adjustment mode unless key held down 
    
    idle:	mov	A,idle_tmr_lo
    	cpi	A,200*1000/1024		;about 200ms expired?
    	brlo	id0			;skip if not
    
    	cbr	cflags,1<<fast_run|1<<fast_adj ;else disable fast adjust mode	
    
    ; Save vol to eeprom & turn off display flash after 1 idle secs
    
    id0:	mov	A,idle_tmr_hi
    	cpi	A,high(1*1000000/1024)	;about 1s expired?
    	brlo	id4			;skip idle stuff if not
    
    	sbrc	cflags,dirty_bit	;skip if already saved eeprom
    	rcall	save_vol		;else do it
    
    	cbr	bflags,1<<d_flash	;turn off display flash
    	
    ; Exit balance/channel modes after 4 idle secs
    
    	cpi	A,high(4*1000000/1024)	;about 4s expired?
    	brne	id3
    
    	sbrc	cflags,b_mode
    	rjmp	id2			;balance mode...
    	
    id1:	sbrs	cflags,c_mode		;channel mode
    	rjmp	id4			;skip if not
    
    id2:	rcall	get_display_vol		;revert to volume display
    	rjmp	id4
    
    ; Blank display (if enabled) after 8 idle secs
    
    id3:	cpi	A,high(8*1000000/1024)
    	brne	id4			;skip unless 8s expired
    
    	sbrc	bflags,d_blank		;skip if not already blanked 
    	rjmp	id4
    
    	ldi	ZL,low(ee_blank)
    	ldi	ZH,high(ee_blank)
    	rcall	ee_read			;get blanking option
    	
    	cpi	A,CH_1			;key code *1* enables blanking...
    	brne	id4
    
    	rcall	display_clear		;blank the display
    	sbr	bflags,1<<d_blank	;flag it
    
    id4:	sbr	aflags,1<<wd_res	;watchdog keep-alive
    	rjmp	mn1			;loop
    
    
    ;**************************************************************************
    ;*
    ;* Rotary encoder event
    ;*
    ;**************************************************************************
    
    rotary_event:
    	cbr	aflags,1<<rot_event
    	cbr	bflags,1<<d_flash	;turn off display flash
    
    	sbrc	cflags,c_mode
    	rjmp	re2			;channel select mode
    
    	rcall	unmute_all		;exit mute mode if muted
    
    	sbrc	cflags,b_mode
    	rjmp	re1			;balance mode
    
    ; Volume adjustment mode
    
    	sbrs	aflags,rot_dir
    	rjmp	volume_down
    	rjmp	volume_up	
    
    ; Balance adjustment mode
    
    re1:	ldi	D,panr
    	sbrs	aflags,rot_dir
    	ldi	D,panl
    	rjmp	pan_lr
    
    ; Channel selection mode
    
    re2:	ldi	D,chup
    	sbrs	aflags,rot_dir
    	ldi	D,chdn
    	rjmp	ch_select
    
    
    ;**************************************************************************
    ;*
    ;* Infrared remote event
    ;*
    ;**************************************************************************
    
    infra_event:	
    	cbr	aflags,1<<ir_event
    	cbr	bflags,1<<d_flash	;turn off display flash
    
    	ldi	ZL,low(ee_ir_sys)
    	ldi	ZH,high(ee_ir_sys)
    	rcall	ee_read			;get programmed address
    
    	mov	B,ir_code
    	andi	B,$7F			;remove the toggle bit
    	cp	A,ir_sys		;our address?
    	breq	ie1			;go if it is
    
    ; Check for TV *punch-through* mute
    
    	tst	ir_sys			;address 0 (TV1)?
    	brne	ie10			;ignore code if not
    
    	mov	B,ir_code		;get received code again
    	andi	B,$7F
    	cpi	B,MUTE_TV1		;is it a punch-through mute?
    	breq	ie3			;go mute if it is
    	rjmp	ie10			;else not for us
    	
    ie1:	cpi	B,MUTE_TV1		;mute/unmute?
    	breq	ie3
    
    ; Check for alternate *mute* keys in TV & CD modes
    
    	cpi	A,TV_ADDR		;TV code?
    	brne	ie2			
    	cpi	B,MUTE_ALT1		;alternate *mute*?
    	breq	ie3
    	rjmp	ie4			;no, go check other
    
    ie2:	cpi	A,CD_ADDR		;CD code?
    	brne	ie4			;no, skip next bit
    	cpi	B,MUTE_ALT2		;yes, alternate *mute* key?
    	brne	ie4			;go check other functions if not
    	
    ; Mute code received, check if repeat
    
    ie3:	rcall	flash_led		;flash the *ack* LED
    	sbrs	aflags,ir_rpt		;ignore if a repeat code
    	rjmp	mute_toggle
    	rjmp	ie10
    
    ; Enable *fast adjust* mode if key is held down for >1 sec
    
    ie4:	sbrs	cflags,fast_run		;skip if timer is already running
    	rjmp	ie5			;else go start it
    	
    	sbrs	aflags,ir_rpt		;continue if repeat of last key
    	rjmp	ie5			;else go restart timer
    	
    	mov	A,fast_tmr_hi
    	cpi	A,high(1*1000000/1024)	;about 1s expired?
    	brne	ie6			;continue if not
    	
    	sbr	cflags,1<<fast_adj	;else flag fast adjust mode
    	rjmp	ie6
    
    ie5:	clr	fast_tmr_lo		;zero fast adjust timer
    	clr	fast_tmr_hi
    	sbr	cflags,1<<fast_run
    
    ie6:	rcall	flash_led		;flash the *ack* LED
    
    ; Not mute, keep looking...
    
    	cpi	B,VOL_UP
    	brne	ie7
    
    	rcall	unmute_all		;exit mute mode if muted
    	rjmp	volume_up		;vol up
    	
    ie7:	cpi	B,VOL_DN
    	brne	ie8
    	rjmp	volume_down		;vol down
    	
    ie8:	cpi	B,CH_1			;channel selection, check valid (1-6)
    	brlo	ie9
    	cpi	B,CH_6+1
    	brsh	ie9
    	ldi	D,chset
    	rjmp	ch_select
    
    ie9:	sbrc	cflags,v_mute
    	rjmp	ie10			;ignore other codes in mute mode
    
    	ldi	D,panr
    	cpi	B,CH_UP
    	breq	pan_lr_ir		;balance right
    	
    	ldi	D,panl
    	cpi	B,CH_DN
    	breq	pan_lr_ir		;balance left
    
    ie10:	rjmp	idle			;ignore
    
    
    ;**************************************************************************
    ;*
    ;* Front panel switch event
    ;*
    ;**************************************************************************
    
    switch_event:
    	cbr	bflags,1<<sw_event
    	sbrs	bflags,sw_stat		;continue if switch press
    	rjmp	se3			;ignore releases...
    
    	lds	A,sw_code
    	cpi	A,1<<bal_sw		;balance or channel switches?
    	brne	se1
    
    ; Toggle balance adjust/display mode
    
    	sbrc	cflags,v_mute
    	rjmp	se3			;ignore balance switch if in mute mode
    
    	sbrc	cflags,b_mode		;skip if not already in balance mode
    	rjmp	se2			;else go exit mode
    
    	rcall	display_bal		;enter balance adjust/display mode	
    	rjmp	se3
    	
    ; Toggle channel adjust/display mode
    
    se1:	sbrc	cflags,c_mode		;skip if not in channel mode
    	rjmp	se2			;else go exit mode
    
    	rcall	display_ch
    	rjmp	se3
    
    se2:	rcall	get_display_vol		;exit balance/channel mode, display volume
    se3:	rjmp	main
    
    
    ;**************************************************************************
    ;*
    ;* Balance left/right
    ;*
    ;**************************************************************************
    
    pan_lr_ir:
    	sbrs	cflags,b_mode		;skip if already in balance mode
    	rjmp	plr8			;else just display (first press)
    
    pan_lr:
    	rcall	get_vol			;get volume for current channel
    	cpi	D,panr
    	breq	plr1
    
    ; Pan left
    	
    	sbrc	C,lr_offset		;skip if left attenuated 
    	rjmp	plr4
    	rjmp	plr2
    
    ; Pan right
    
    plr1:	sbrs	C,lr_offset		;skip if right attenuated
    	rjmp	plr4
    
    plr2:	tst	B			;zero offset?
    	brne	plr6
    
    plr3:	ldi	D,1<<lr_offset
    	eor	C,D			;switch direction...
    
    plr4:	inc	B			;increase left/right attenuation
    	breq	plr5			;abort if already at max
    
    	sbrs	cflags,fast_adj		;skip if fast adjust (1.5dB) mode
    	rjmp	plr7			;else go update
    
    	cpi	B,254			;at least two points from top?
    	brsh	plr7			;single step (0.5dB) if not
    	
    	subi	B,-2			;for 3x (1.5dB) step
    	rjmp	plr7
    
    plr5:	dec	B
    	sbr	bflags,1<<d_flash	;set to flash the display
    	rjmp	plr8
    
    plr6:	dec	B			;decrease right attenuation
    	sbrs	cflags,fast_adj		;skip if fast adjust (1.5dB) mode	
    	rjmp	plr7			;else go update
    	
    	cpi	B,2			;at least two points from bottom?
    	brlo	plr7			;single step (0.5dB) if not
    	subi	B,2			;for 3x (1.5dB) step
    
    plr7:	rcall	put_vol			;update volume
    plr8:	rcall	display_bal
    	rjmp	main			;done
    
    
    ;**************************************************************************
    ;*
    ;* Volume up
    ;*
    ;**************************************************************************
    
    volume_up:
    	rcall	get_vol
    	inc	A			;increase volume
    	brne	vu2
    	
    vu1:	dec	A			;already at max...
    	clr	d_timer			;to synchronise the flash...
    	sbr	bflags,1<<d_flash	;set to flash the display
    	rjmp	vu5
    
    vu2:	sbic	PINB,JP2		;skip if set for 0.5dB adjustments
    	rjmp	vu3			;must be 1.5dB...
    	
    	sbrs	cflags,fast_adj		;skip if fast adjust (1.5dB) mode
    	rjmp	vu4			;else go update
    
    vu3:	cpi	A,254			;at least two points from top?
    	brsh	vu4			;single step (0.5dB) if not
    	subi	A,-2			;for 3x (1.5dB) step
    
    vu4:	rcall	put_vol			;update volume
    vu5:	rcall	get_display_vol
    	rjmp	main			;done
    
    
    ;**************************************************************************
    ;*
    ;* Volume down
    ;*
    ;**************************************************************************
    
    volume_down:
    	rcall	get_vol
    	tst	A
    	breq	vd4			;quit if already at min
    
    vd1:	dec	A
    	sbic	PINB,JP2		;skip if 0.5dB adjustments
    	rjmp	vd2			;must be 1.5dB...
    
    	sbrs	cflags,fast_adj	
    	rjmp	vd3
    
    vd2:	cpi	A,2			;at least two points from bottom?
    	brlo	vd3			;single step (0.5dB) if not
    	subi	A,2			;for 3x (1.5dB) step
    
    vd3:	rcall	put_vol			;update volume
    vd4:	rcall	get_display_vol
    	rjmp	main			;done
    
    
    ;**************************************************************************
    ;*
    ;* Mute/unmute system
    ;*
    ;**************************************************************************
    
    mute_toggle:
    	sbrs	cflags,v_mute		;skip if currently muted...
    	rjmp	mt1
    
    	rcall	unmute_all		;restore volume level
    	rjmp	mt2
    
    mt1:	rcall	mute_all		;mute all PGAs
    mt2:	rjmp	main
    
    
    ;**************************************************************************
    ;*
    ;* Channel selection
    ;*
    ;**************************************************************************
    
    ch_select:
    	cbr	cflags,1<<b_mode	;exit balance mode
    	cbi	PORTA,PA0		;ensure left *DP* bit is off
    	sbr	cflags,1<<c_mode	;enter channel mode
    	mov	A,B			;A=channel #
    
    	cpi	D,chset
    	breq	cs2
    
    	lds	A,pga_ch		;get current channel
    	cpi	D,chdn
    	breq	cs1			;go if channel down
    	
    	inc	A			;must be channel up...
    	rjmp	cs2
    
    cs1:	dec	A
    	tst	A
    	breq	cs4			;ch #1 is min
    		
    cs2:	lds	B,pga_cnt		;get number of actual channels
    	cp	B,A
    	brlo	cs4			;ignore if exceeds actual 
    
    cs3:	sts	pga_ch,A		;else update as current
    	rcall	save_ch			;save in EEPROM
    cs4:	lds	A,pga_ch
    	rcall	display			;display channel number (Cn)
    	rjmp	main			;done
    
    
    ; Subroutines start here...
    
    ;**************************************************************************
    ;*
    ;* Save current channel to EEPROM
    ;*
    ;* Entry: pga_ch = current channel (1-6)
    ;*
    ;**************************************************************************
    
    save_ch:
    	ldi	ZL,low(ee_ch)		;channel # in EEPROM
    	ldi	ZH,high(ee_ch)
    	lds	A,pga_ch
    	rcall	ee_write		;save current channel #
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Save current left & right channel volume to EEPROM
    ;*
    ;* Entry: pga_ch = current channel (1-6)
    ;*
    ;**************************************************************************
    
    save_vol:
    	ldi	XL,low(vol_data)	;vol data in RAM
    	ldi	XH,high(vol_data)
    	lds	A,pga_ch
    	dec	A
    	mov	B,A
    	lsl	A			;entries are 3 bytes long...
    	add	A,B
    
    	add	XL,A
    	clr	B
    	adc	XH,B
    
    	ldi	ZL,low(ee_vol)		;vol data in EEPROM
    	ldi	ZH,high(ee_vol)
    
    	add	ZL,A
    	adc	ZH,B
    
    	ld	A,X+
    	rcall	ee_write		;save volume
    	adiw	ZH:ZL,1
    	ld	A,X+
    	rcall	ee_write		;save offset
    	adiw	ZH:ZL,1
    	ld	A,X+
    	rcall	ee_write		;save flags
    	cbr	cflags,1<<dirty_bit
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Restore volume & channel data from EEPROM
    ;*
    ;**************************************************************************
    
    restore_vol:
    	ldi	ZL,low(ee_ch)		;vol data in EEPROM
    	ldi	ZH,high(ee_ch)
    	rcall	ee_read
    	sts	pga_ch,A		;becomes current channel
    
    	ldi	XL,low(vol_data)	;put vol data here
    	ldi	XH,high(vol_data)
    	lds	B,pga_cnt		;number of PGAs installed
    
    rv1:	adiw	ZH:ZL,1
    	rcall	ee_read
    	st	X+,A			;copy vol data
    	adiw	ZH:ZL,1	
    	rcall	ee_read
    	st	X+,A			;copy offset data
    	adiw	ZH:ZL,1	
    	rcall	ee_read
    	st	X+,A			;copy channel flags
    	dec	B
    	brne	rv1			;do for all channels (PGAs)
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Mute all channels
    ;*
    ;**************************************************************************
    
    mute_all:
    	sbr	cflags,1<<v_mute
    	cbi	PORTB,pga_mute		;mute local device
    	rcall	write_pga		;write to PGAs
    	rcall	get_display_vol
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Unmute all channels
    ;*
    ;**************************************************************************
    
    unmute_all:
    	sbrs	cflags,v_mute
    	rjmp	um1	
    
    	cbr	cflags,1<<v_mute	;reset muted flag
    	rcall	write_pga		;write to PGAs
    	cbi	PORTA,PA5		;ensure right *DP* bit is off
    	sbi	PORTB,pga_mute		;release local MUTE pin
    	rcall	get_display_vol
    um1:	ret
    		
    
    ;**************************************************************************
    ;*
    ;* Display balance level
    ;*
    ;**************************************************************************
    
    display_bal:
    	cbr	cflags,1<<c_mode	;exit channel mode
    	sbr	cflags,1<<b_mode	;enter balance mode
    	rcall	get_vol
    
    	cpi	B,2
    	breq	db1
    	
    	cpi	B,1
    	breq	db1
    	
    	cpi	B,0
    	brne	db4
    
    	ldi	C,$0A
    	ldi	A,$0A
    	rjmp	db3			;display *--*
    
    db1:	sbrc	C,lr_offset
    	rjmp	db2
    
    	ldi	C,$0C
    	ldi	A,$0A
    	rjmp	db3			;display * -*
    
    db2:	ldi	C,$0A
    	ldi	A,$0C			;display *- *
    
    db3:	rcall	disp_raw
    	rjmp	db5
    
    db4:	mov	A,B			;get offset
    	rcall	dpv1
    db5:	ret
    
    
    ;**************************************************************************
    ;*
    ;* Display channel number
    ;*
    ;**************************************************************************
    
    display_ch:
    	cbr	cflags,1<<b_mode	;may be in balance mode so exit
    	cbi	PORTA,PA0		;ensure left *DP* bit is off
    	
    	sbr	cflags,1<<c_mode	;enter channel mode
    	lds	A,pga_ch		;get current channel
    	rcall	display			;and display it
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Get and display volume
    ;* 
    ;* Retrieves the volume for the currently selected PGA and displays it.
    ;*
    ;**************************************************************************
    
    get_display_vol:
    	rcall	get_vol			;fall thru to display_vol
    
    ;**************************************************************************
    ;*
    ;* Display volume (input range is 0-255, display range is 0-85)
    ;*
    ;* Input: A = volume
    ;*
    ;**************************************************************************
    
    display_vol:
    	cbr	cflags,1<<b_mode|1<<c_mode ;exit *balance* or *channel* modes
    	cbi	PORTA,PA0		;ensure left *DP* bit is off
    
    dpv1:	push	B
    	mov	B,A
    	rcall	divide_by_3		;returns A = B/3
    	rcall	display			;display it
    	pop	B
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Get volume (from buffer)
    ;*
    ;* Exit: A = volume  (0-255) for current pga_ch
    ;*       B = offset  (0-255)
    ;*       C = vflags
    ;*
    ;**************************************************************************
    
    get_vol:
    	ldi	ZL,low(vol_data)	;vol data in RAM
    	ldi	ZH,high(vol_data)
    
    	lds	A,pga_ch
    	dec	A
    	mov	B,A
    	lsl	A			;entries are 3 bytes long...
    	add	A,B
    
    	add	ZL,A
    	clr	B
    	adc	ZH,B
    
    	ld	A,Z+			;volume
    	ld	B,Z+			;offset
    	ld	C,Z+			;flags
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Put volume (in buffer) & write to PGAs
    ;*
    ;* Entry: A = volume  (0-255) for current pga_ch
    ;*        B = offset  (0-255)
    ;*        C = vflags
    ;*
    ;**************************************************************************
    
    put_vol:
    	ldi	ZL,low(vol_data)	;vol data in RAM
    	ldi	ZH,high(vol_data)
    	push	A
    	push	B
    
    	lds	A,pga_ch
    	dec	A
    	mov	B,A
    	lsl	A			;entries are 3 bytes long...
    	add	A,B	
    
    	add	ZL,A
    	clr	B
    	adc	ZH,B
    
    	pop	B
    	pop	A
    	st	Z+,A			;volume
    	st	Z+,B			;offset
    	st	Z+,C			;flags
    	
    	rcall	write_pga		;update PGAs
    	sbr	cflags,1<<dirty_bit	;not saved yet...
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Determine number of connected PGAs
    ;*
    ;* Exit: C = PGA count
    ;* 
    ;**************************************************************************
    
    get_pga:
    	ldi	ZL,low(spi_buff)	;spi data buffer
    	ldi	ZH,high(spi_buff)
    	ldi	B,24			;tx + rx buffer size
    	clr	A
    	
    gpg1:	st	Z+,A			;zero the buffer
    	dec	B
    	brne	gpg1
    
    	ldi	ZL,low(spi_buff)
    	ldi	ZH,high(spi_buff)
    	ldi	A,1			;for a single 16-bit transfer
    
    	sts	pga_cnt,A
    	st	Z+,A
    	ldi	A,2
    	st	Z+,A
    	clr	C
    	
    gpg2:	rcall	spi_write		;write test data to the PGA(s)
    	ldd	A,Z+10			;get data read (left channel)
    	cpi	A,1			;equals write?
    	brne	gpg3			;skip if not
    
    	ldd	A,Z+11			;get data read (right channel)
    	cpi	A,2
    	breq	gpg4			;go if match
    
    gpg3:	inc	C
    	cpi	C,7			;tested for max PGAs?
    	brne	gpg2			;keep trying if not
    
    	clr	C
    gpg4:	sts	pga_cnt,C		;set number connected
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Format spi buffer with volume data & initiate write
    ;*
    ;**************************************************************************
    
    write_pga:
    	ldi	XL,low(vol_data)	;vol data in RAM
    	ldi	XH,high(vol_data)
    	ldi	ZL,low(spi_buff)	;spi data buffer
    	ldi	ZH,high(spi_buff)
    
    	lds	A,pga_cnt
    wp1:	push	A
    
    	ld	A,X+			;left vol
    	mov	B,A			;right vol
    	ld	C,X+			;offset
    	ld	D,X+			;vflags
    
    	tst	A
    	breq	wp3			;zero offset
    
    	sbrs	D,lr_offset
    	rjmp	wp2
    
    ; Panned left, so subtract offset from right channel
    
    	sub	B,C
    	brcc	wp3
    	clr	B
    	rjmp	wp3
    
    ; Panned right, so subtract offset from left channel
    
    wp2:	sub	A,C
    	brcc	wp3
    	clr	A
    
    ; Now ignore all of that if muted...
    
    wp3:	sbrs	cflags,v_mute	
    	rjmp	wp4
    	
    	clr	A
    	clr	B
    
    wp4:	st	Z+,A			;left vol to spi_buff
    	st	Z+,B			;right vol to spi_buff
    
    	pop	A
    	dec	A
    	brne	wp1			
    
    
    ;**************************************************************************
    ;*
    ;* Initiate spi transfer & wait until done
    ;*
    ;**************************************************************************
    
    spi_write:
    	sbr	aflags,1<<spi_req	;initiate the write
    
    spi1:	sbrc	aflags,spi_req
    	rjmp	spi1			;wait till done
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* 7-segment display write
    ;*
    ;* Entry: A = data to display
    ;*
    ;**************************************************************************
    
    ; Convert input to BCD in C,A
    
    display:
    	clr	C			;becomes tens
    ds1:	subi	A,10
    	brcs	ds2
    
    	inc	C			;bump MSD
    	rjmp	ds1
    
    ds2:	subi	A,-10			;adjust units
    
    ; Lookup *units* 7-seg bit pattern
    
    disp_raw:
    	ldi	ZH,high(units_table*2)	;units lookup table
    	ldi	ZL,low(units_table*2)
    	lsl	A			;for 2-byte entries
    	add	ZL,A			;input is index
    	clr	A
    	adc	ZH,A
    
    .if !debug
    	lpm	A,Z+			;get port C...
    	lpm	B,Z			;...and port A bit patterns
    .else
    	cli				;R0 is used by interrputs!
    	lpm				;get port C...
    	adiw	ZH:ZL,1
    	mov	A,R0
    	lpm				;...and port A bit patterns
    	mov	B,R0
    	sei	
    .endif
    
    ; Display *C* in the tens position in channel select mode
    
    	sbrc	cflags,c_mode
    	ldi	C,$0B			;offset to *C* pattern
    
    ; Lookup *tens* 7-seg bit pattern
    
    	ldi	ZH,high(tens_table*2)	;tens lookup table
    	ldi	ZL,low(tens_table*2)
    	lsl	C			;for 2-byte entries
    	add	ZL,C			;input is index
    	clr	C
    	adc	ZH,C
    
    ; Merge the bit patterns and write to ports A & C
    
    .if !debug
    	lpm	C,Z+			;get port C *tens* bits
    .else
    	cli				;R0 is used by interrputs!
    	lpm
    	adiw	ZH:ZL,1
    	mov	C,R0
    	sei
    .endif
    	or	A,C			;merge with *units* bits
    
    .if !debug
    	lpm	C,Z			;get port A *tens* bits
    .else
    	cli
    	lpm
    	mov	C,R0
    	sei
    .endif
    	or	B,C			;merge with *units* bits
    
    ds3:	out	PORTC,A			;write the results...
    	in	A,PORTA
    	andi	A,0b00100001		;keep *DP* bits
    	or	A,B
    	out	PORTA,A
    	cbr	bflags,1<<d_blank	;not blanked!
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* 7-segment display clear
    ;*
    ;**************************************************************************
    
    display_clear:
    	clr	A
    	out	PORTC,A			;write zeros to blank
    	out	PORTA,A
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Fixed 8-bit divide-by-3
    ;*
    ;* Entry: B = hex byte
    ;* Exit:  A = dividend, B = remainder
    ;* Uses:  C,D
    ;*
    ;**************************************************************************
    
    divide_by_3:
    	clr	A
    	ldi	D,3*16
    	rcall	dv1
    	ldi	D,3
    
    dv1:	ldi	C,-1
    dv2:	sub	B,D
    	inc	C
    	brcc	dv2
    	
    	add	B,D
    	swap	A
    	or	A,C
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Wait for infrared code
    ;*
    ;**************************************************************************
    
    wait_ir:
    	sbrs	aflags,ir_event
    	rjmp	wait_ir
    	
    	cbr	aflags,1<<ir_event
    	sbrc	aflags,ir_rpt
    	rjmp	wait_ir			;ignore if repeat code
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Flash LED *B* times
    ;*
    ;**************************************************************************
    
    flash_it:
    	rcall	flash_led
    
    fi1:	sbic	PIND,ack_led
    	rjmp	fi1			;wait for LED off
    
    	ldi	A,250
    	rcall	delay			;delay for about 250ms
    
    	dec	B
    	brne	flash_it
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Flash LED
    ;*
    ;**************************************************************************
    
    flash_led:
    	push	A
    	ldi	A,50			;about 50ms on time
    	sts	led_timer,A
    	sbi	PORTD,ack_led		;switch LED on
    	pop	A
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Timed delay
    ;*
    ;**************************************************************************
    
    delay:
    	sts	led_timer,A
    dy1:	lds	A,led_timer
    	tst	A
    	brne	dy1			;A * 1.024ms
    	ret
    	
    	
    ;**************************************************************************
    ;*
    ;* Initialise EEPROM
    ;*
    ;**************************************************************************
    
    init_eeprom:
    	ldi	ZL,low(ee_ir_sys)
    	ldi	ZH,high(ee_ir_sys)
    
    	ldi	A,0
    	rcall	ee_write		;default infrared address
    	adiw	ZH:ZL,1
    
    	ldi	A,1
    	rcall	ee_write		;default channel
    	adiw	ZH:ZL,1
    
    	clr	A
    	ldi	B,3*6
    ine1:	rcall	ee_write		;zero vol data for all channels	
    	adiw	ZH:ZL,1
    	dec	B
    	brne	ine1
    
    	ldi	ZL,low(ee_magic)	;write magic number
    	ldi	ZH,high(ee_magic)
    	ldi	A,$5A
    	rcall	ee_write
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* EEPROM random read/write routines
    ;*
    ;* Entry: Z = address, A = data (writes)
    ;* Exit : A = data (reads)
    ;*
    ;**************************************************************************
    
    ee_read:
    	sbic	EECR,EEWE
    	rjmp	ee_read			;loop until last cycle complete
    
    	out	EEARL,ZL		;write address
    	out	EEARH,ZH
    	sbi	EECR,EERE		;set read strobe
    	in	A,EEDR			;get data
    	ret
    	
    ee_write:
    	sbic	EECR,EEWE
    	rjmp	ee_write		;loop until last cycle complete
    
    	out	EEARL,ZL		;write address
    	out	EEARH,ZH
    	out	EEDR,A			;write data
    	cli				;interrupts off for the next bit...
    	sbi	EECR,EEMWE		;set master write enable
    	sbi	EECR,EEWE		;set write strobe
    	sei
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Timer/counter 0 overflow interrupt handler
    ;*
    ;* Tick interval is 32us (1/fOSC x 128)
    ;*
    ;**************************************************************************
    
    tmr0_irq:
    	in	status,SREG		;save state
    	push	A			;save used global
    	push	B
    	push	ZH
    	push	ZL
    
    	ldi	A,128			;reload timer 0 up count
    	out	TCNT0,A
    	
    ;**************************************************************************
    ;* Do 32us tasks
    ;**************************************************************************
    
    	rcall	spi_io			;serial i/o
    	inc	irate_tmr
    	sbrs	irate_tmr,0
    	rjmp	tq1
    
    ;**************************************************************************
    ;* Do 64us tasks (interleaved to reduce overhead)
    ;**************************************************************************
    
    	rcall	infrared		;infrared receive state machine
    	rjmp	tq7			;exit
    
    tq1:
    	rcall	switch			;switch input
    	rcall	rotary			;rotary encoder input
    
    ;**************************************************************************
    ;* Do 1.024ms tasks
    ;**************************************************************************
    
    tq2:	mov	A,irate_tmr
    	cpi	A,32			;32 * 32us = 1.024ms 
    	brne	tq7
    
    	clr	irate_tmr		;reset timer
    
    	lds	A,led_timer
    	dec	A
    	sts	led_timer,A
    	brne	tq3
    	cbi	PORTD,ack_led		;switch LED off
    	
    tq3:	inc	idle_tmr_lo		;idle timer
    	brne	tq4
    	inc	idle_tmr_hi
    
    tq4:	inc	fast_tmr_lo		;*fast adjust* timer
    	brne	tq5
    	inc	fast_tmr_hi
    
    tq5:	dec	sw_timer		;switch filter timer
    	dec	d_timer			;display flash timer
    	brne	tq6
    	rcall	flash
    
    tq6:	sbrs	aflags,wd_res		;keep-alive set?
    	rjmp	tq7
    
    	cbr	aflags,1<<wd_res
    	wdr				;kick the *dog
    
    tq7:	pop	ZL			;restore used registers
    	pop	ZH
    	pop	B
    	pop	A
    	out	SREG,status		;and flags
    	reti
    
    
    ;**************************************************************************
    ;*
    ;* Flash display
    ;*
    ;**************************************************************************
    
    flash:
    	sbrc	bflags,d_state		;skip if not currently blanked
    	rjmp	fl2			;blanked, so go restore
    
    	sbrc	bflags,d_flash		;skip if flash not enabled
    	rjmp	fl1			;else go flash
    
    	sbrc	cflags,v_mute
    	rjmp	fl0			;go if muted
    
    	sbrs	cflags,b_mode		;skip if in *balance* mode
    	rjmp	fl3			;else quit
    
    ; Toggle the left *DP* bit (indicates *balance* mode)
    
    	in	A,PORTA
    	ldi	B,0b00000001
    	eor	A,B			;toggle left *DP*
    	out	PORTA,A
    	rjmp	fl3
    
    ; Toggle the right *DP* bit (indicates muted)
    
    fl0:	in	A,PORTA
    	ldi	B,0b00100000
    	eor	A,B			;toggle right *DP*
    	out	PORTA,A	
    	rjmp	fl3
    
    ; Blank the display
    
    fl1:	in	A,PORTC			;save port state
    	sts	d_portc,A
    	in	A,PORTA
    	sts	d_porta,A
    
    	clr	A
    	out	PORTC,A			;now write zeros to blank
    	out	PORTA,A
    	sbr	bflags,1<<d_state	;flag blanked
    	rjmp	fl3
    
    ; Restore the display
    
    fl2:	lds	A,d_portc
    	out	PORTC,A
    	lds	A,d_porta
    	out	PORTA,A
    	cbr	bflags,1<<d_state
    fl3:	ret
    
    
    ;**************************************************************************
    ;*
    ;* Switch input
    ;*
    ;**************************************************************************
    
    switch:
    .if !debug
    	in	A,PINE			;get port state
    .else
    	in	A,PIND			;get port state
    .endif
    	sbrs	bflags,sw_run		;filter running?
    	rjmp	ss2			;go check for switch press if not
    
    	lds	B,sw_code
    	and	A,B			;switch open or closed?
    	breq	ss4			;go reset timer if closed
    
    ss1:	tst	sw_timer		;check if timer expired
    	brne	ss5			;exit if not
    
    	cbr	bflags,1<<sw_run|1<<sw_stat
    	sbr	bflags,1<<sw_event
    	rjmp	ss5
    
    ss2:	ldi	B,1<<ch_sw
    	and	A,B
    	breq	ss3			;switch down
    
    .if !debug
    	in	A,PINE
    .else
    	in	A,PIND
    .endif
    	ldi	B,1<<bal_sw
    	and	A,B
    	brne	ss5			;both switches up
    
    ss3:	sts	sw_code,B		;save the switch code
    	sbr	bflags,1<<sw_run|1<<sw_stat|1<<sw_event ;start filter & flag press
    
    ss4:	ldi	A,30			;30ms debounce timer
    	mov	sw_timer,A
    ss5:	ret
    
    
    ;**************************************************************************
    ;*
    ;* Rotary encoder input
    ;*
    ;* The effects of contact bounce are eliminated by qualifying
    ;* the operation of one switch against the other, as follows:
    ;*
    ;* Set rot_f1 when A=0 & B=0, clear when A=1 & B=1
    ;* Set rot_f2 when A=1 & B=0, clear when A=0 & B=1
    ;*
    ;* Trailing edge of rot_f1 sets rot_event, rot_f2 = rot_dir
    ;*
    ;* NOTE: requires an encoder mechanism of equal cycles & detents!
    ;*
    ;**************************************************************************
    
    rotary:
    	in	A,PIND
    	andi	A,1<<rot_a|1<<rot_b
    	brne	rs1
    
    ; A=0 & B=0
    
    	sbrc	aflags,rot_f1
    	rjmp	rs4	
    
    	bst	aflags,rot_f2		;becomes direction bit
    	bld	aflags,rot_dir
    	sbr	aflags,1<<rot_f1|1<<rot_event
    	rjmp	rs4
    
    rs1:	cpi	A,1<<rot_a|1<<rot_b
    	brne	rs2
    	
    ; A=1 & B=1
    
    	cbr	aflags,1<<rot_f1
    	rjmp	rs4
    
    rs2:	cpi	A,1<<rot_a
    	brne	rs3
    
    ; A=1 & B=0
    
    	sbr	aflags,1<<rot_f2
    	rjmp	rs4
    
    ; A=0 & B=1
    
    rs3:	cbr	aflags,1<<rot_f2
    rs4:	ret	
    
    
    ;**************************************************************************
    ;*
    ;* SPI state machine
    ;*
    ;**************************************************************************
    
    ; Start (resume) transfer in the correct state
    
    spi_io:
    	ldi	ZH,high(spi_jmp_tbl)
    	ldi	ZL,low(spi_jmp_tbl)
    
    	add	ZL,spi_state		;state number becomes index
    	clr	A
    	adc	ZH,A			;find the correct fragment
    	ijmp				; and execute it
    		
    spi_jmp_tbl:
    	rjmp	spi_s0
    	rjmp	spi_s1
    	rjmp	spi_s2
    	rjmp	spi_s3
    
    
    ;**************************************************************************
    ;
    ; SPI State 0
    ;
    ; Wait for foreground request, then initialise
    ;
    ;**************************************************************************
    
    spi_s0:
    	sbrs	aflags,spi_req		;skip if transfer request
    	rjmp	s0_0			;else just exit
    
    	lds	A,pga_cnt		;# of PGAs on the bus
    	mov	spi_cnt,A		;words to transfer
    
    	ldi	YL,low(spi_buff)	;data buffer
    	ldi	YH,high(spi_buff)
    	lsl	A			;x2 to get byte count
    	add	YL,A			;add as offset to send high word(s) first 
    	clr	A
    	adc	YH,A
    
    	ldi	spi_bits,16		;init word size
    	ld	spi_pipe_hi,-Y		;load the pipe - right channel
    	ld	spi_pipe_lo,-Y		;              - left channel
    	cbi	PORTB,sck		;clock low
    	sbi	DDRB,sck		;make clock & data lines outputs 
    	sbi	DDRB,sdo
    
    	inc	spi_state		;to state #1
    s0_0:	ret
    
    
    ;**************************************************************************
    ;
    ; SPI State 1
    ;
    ; Write data to bus, clock low  [pga-->] 
    ; 
    ;**************************************************************************
    
    spi_s1:
    	cbi	PORTB,sck		;clock low
    	cbi	PORTD,pga_cs		;assert pga chip select (first cycle)
    
    	lsl	spi_pipe_lo
    	rol	spi_pipe_hi		;msb first
    	brcc	s1_0	
    
    	sbi	PORTB,sdo		;data line high
    	rjmp	s1_1
    
    s1_0:	cbi	PORTB,sdo		;data line low
    
    s1_1:	inc	spi_state		;to state #2
    	ret
    
    
    ;**************************************************************************
    ;
    ; SPI State 2
    ;
    ; Read data from bus, clock high  [-->pga] 
    ; 
    ;**************************************************************************
    
    spi_s2:	sbis	PINB,sdi		;skip if data input low
    	rjmp	s2_0
    
    	ldi	A,1
    	or	spi_pipe_lo,A		;high, so set lsb
    
    s2_0:	sbi	PORTB,sck		;clock high
    
    	dec	spi_bits		;done 16 bits?
    	brne	s2_2			;continue if not
    	
    	std	Y+13,spi_pipe_hi	;save received word
    	std	Y+12,spi_pipe_lo
    	
    	dec	spi_cnt			;done all pgas?
    	brne	s2_1			;more to do...
    
    	inc	spi_state		;else bump to state #3
    	rjmp	s2_3
    
    s2_1:	ldi	spi_bits,16		;reload word size
    	ld	spi_pipe_hi,-Y		;and pipe - right channel
    	ld	spi_pipe_lo,-Y		;         - left channel
    
    s2_2:	dec	spi_state		;loop back to state #1
    s2_3:	ret
    
    
    ;**************************************************************************
    ;
    ; SPI State 3
    ;
    ; End transfer
    ; 
    ;**************************************************************************
    
    spi_s3: cbi	PORTB,sck		;clock low
    	rjmp	s3_0			;waste a cycle or two...
    
    s3_0:	sbi	PORTD,pga_cs		;deassert pga chip select
    
    	cbi	DDRB,sck		;clock & data bits to inputs 
    	cbi	DDRB,sdo
    	sbi	PORTB,sck		;enable pullups
    	sbi	PORTB,sdo
    
    	cbr	aflags,1<<spi_req	;transfer done, clear request bit
    	clr	spi_state
    	ret
    
    
    ;**************************************************************************
    ;*
    ;* Infrared receive state machine (must be called every 64us)
    ;*
    ;**************************************************************************
    
    infrared:
    	sbrs	bflags,sw_run		;switch press will pull ir_rxd low...	
    	rjmp	ix0			;continue if not pressed
    
    	clr	ir_state		;switch pressed, reset state
    	ret				;and exit
    	
    ix0:	inc	ir_timer		;bump ir protocol (bit) timer
    
    ; Start (resume) ir decode in the correct state
    
    	ldi	ZH,high(rc5_jmp_tbl)
    	ldi	ZL,low(rc5_jmp_tbl)
    
    	add	ZL,ir_state		;state number becomes index
    	clr	A
    	adc	ZH,A			;find the correct fragment
    	ijmp				; and execute it
    		
    rc5_jmp_tbl:
    	rjmp	rc5_s0
    	rjmp	rc5_s1
    	rjmp	rc5_s2
    	rjmp	rc5_s3
    	rjmp	rc5_s4
    	rjmp	rc5_s5
    	rjmp	rc5_s6
    	rjmp	rc5_s7
    	
    
    ;**************************************************************************
    ;
    ; RC5 State 0
    ;
    ; Wait for line idle (high)
    ;
    ;**************************************************************************
    
    rc5_s0:
    .if debug
    	sbis	PIND,ir_rxd		;wait for line high
    .else
    	sbis	PINE,ir_rxd		;wait for line high
    .endif
    	rjmp	exit_state
    			
    	clr	ir_timer		;init timer
    	rjmp	next_state		;move to state 1 & exit
    
    
    ;**************************************************************************
    ;
    ; RC5 State 1
    ;
    ; Establish line is idle (high) for 5ms before waiting for a start bit
    ;
    ;**************************************************************************
    
    rc5_s1:
    .if debug
    	sbis	PIND,ir_rxd		;skip if line high
    .else
    	sbis	PINE,ir_rxd		;skip if line high
    .endif
    	rjmp	reset_state		;else reset & exit
    	
    	cpi	ir_timer,78		;idle high for 5ms?
    	brlo	r1_0			;keep waiting if not
    	rjmp	next_state		;else move to state 2 & exit
    
    r1_0:	rjmp	exit_state
    	
    
    ;**************************************************************************
    ;
    ; RC5 State 2
    ;
    ; Wait (forever) for start bit
    ;
    ;**************************************************************************
    
    rc5_s2:
    .if debug
    	sbic	PIND,ir_rxd		;skip if start bit detected
    .else
    	sbic	PINE,ir_rxd		;skip if start bit detected
    .endif
    	rjmp	exit_state
    
    r2_0:	clr	ir_timer		;init timer
    	rjmp	next_state		;move to state 3 & exit
    
    
    ;**************************************************************************
    ;
    ; RC5 State 3
    ;
    ; Wait for rising edge of first start bit
    ;
    ;**************************************************************************
    
    rc5_s3:
    .if debug
    	sbis	PIND,ir_rxd		;skip if line returned high
    .else
    	sbis	PINE,ir_rxd		;skip if line returned high
    .endif
    	rjmp	r3_0			;else go check for timeout
    
    	clr	ir_timer		;init timer
    	rjmp	next_state		;move to state 4 & exit
    
    r3_0:	cpi	ir_timer,31		;more than 2ms?
    	brlo	exit_state		;exit to keep sampling
    	rjmp	reset_state		;else reset
    
    
    ;**************************************************************************
    ;
    ; RC5 State 4
    ;
    ; Wait for trailing edge of second start bit to synchronise timing
    ;
    ;**************************************************************************
    
    rc5_s4:
    .if debug
    	sbic	PIND,ir_rxd		;skip when line goes low
    .else
    	sbic	PINE,ir_rxd		;skip when line goes low
    .endif
    	rjmp	r4_0			;else go check for timeout
    	
    	ldi	A,12			;init bit count
    	mov	ir_bit_cnt,A
    	clr	ir_sys			;zero the bit store
    	clr	ir_cmd	
    
    	clr	ir_timer		;init timer
    	rjmp	next_state		;more to state 5 & exit
    
    r4_0:	cpi	ir_timer,31		;more than 2ms?
    	brlo	r4_1			;skip to keep sampling
    	rjmp	reset_state		;timeout, reset
    r4_1:	rjmp	exit_state
    
    
    ;**************************************************************************
    ;
    ; RC5 State 5
    ;
    ; Sample the line at 1/4 bit time and store result
    ;
    ;**************************************************************************
    
    rc5_s5:
    	cpi	ir_timer,21		;3/4 bit time from last edge (1.34ms)?
    	brlo	r5_1			;keep waiting if not
    
    	inc	ir_state		;assume will be low, next state = 6
    .if debug
    	sbic	PIND,ir_rxd		;skip if sampled low
    .else
    	sbic	PINE,ir_rxd		;skip if sampled low
    .endif
    	rjmp	r5_2
    	
    	clc				;to shift in a low bit
    r5_0:	rol	ir_cmd
    	rol	ir_sys
    
    	clr	ir_timer		;init timer
    r5_1:	rjmp	exit_state
    
    r5_2:	inc	ir_state		;sampled high, next state = 7
    	sec
    	rjmp	r5_0			;go shift in the high bit
    
    
    ;**************************************************************************
    ;
    ; RC5 State 6
    ;
    ; Bit was a low, so wait for rising edge to syncronize timing
    ;
    ;**************************************************************************
    
    rc5_s6:
    .if debug
    	sbis	PIND,ir_rxd		;skip if sampled high
    .else
    	sbis	PINE,ir_rxd		;skip if sampled high
    .endif
    	rjmp	r6_3			;else go check for timeout
    	
    r6_0:	dec	ir_bit_cnt		;done all bits?
    	brne	r6_2			;not yet...
    	
    	mov	A,ir_cmd		;get all the bits...
    	rol	A
    	rol	ir_sys			;...in the right places
    	rol	A
    	rol	ir_sys
    
    	bst	ir_sys,5		;move toggle bit...
    	bld	ir_cmd,7		;to command byte MSB
    	
    	clt
    	bld	ir_sys,5
    	bld	ir_cmd,6		;clear unused bits
    
    	cbr	aflags,1<<ir_rpt
    	cp	ir_code,ir_cmd		;same code as last time?
    	brne	r6_1			;skip if not
    	sbr	aflags,1<<ir_rpt	;else flag key repeat
    	
    r6_1:	mov	ir_code,ir_cmd	
    	sbr	aflags,1<<ir_event	;mail to the foreground
    	rjmp	reset_state		;reset machine & exit
    	
    r6_2:	ldi	A,5			;more to do
    	mov	ir_state,A		;loop back to get another bit
    	clr	ir_timer
    	rjmp	exit_state
    
    r6_3:	cpi	ir_timer,35		;5/4 bit time from edge (2.2ms)?
    	brlo	exit_state		;keep sampling if not
    	rjmp	reset_state		;else timeout waiting for middle edge
    
    
    ;**************************************************************************
    ;
    ; RC5 State 7
    ;
    ; Bit was a high, so wait for falling edge to syncronize timing
    ;
    ;**************************************************************************
    
    rc5_s7:
    .if debug
    	sbic	PIND,ir_rxd		;skip if sampled low
    .else
    	sbic	PINE,ir_rxd		;skip if sampled low
    .endif
    	rjmp	r6_3			;else go check for timeout
    	rjmp	r6_0
    
    
    ;**************************************************************************
    
    ; Common exit stuff...
    
    next_state:
    	inc	ir_state		;bump to next state
    	rjmp	exit_state
    
    reset_state:
    	clr	ir_state		;reset machine
    
    exit_state:
    	ret
    
    
    ;**************************************************************************
    ;**************************************************************************
    ;*
    ;* DATA
    ;*
    ;**************************************************************************
    ;**************************************************************************
    
    ; 7-segment display lookup table,
    ; organised as port C, port A.
    
    units_table:
    .db	0b00110100, 0b00011100		;0
    .db	0b00000100, 0b00001000		;1
    .db	0b00111000, 0b00001100		;2
    .db	0b00011100, 0b00001100		;3
    .db	0b00001100, 0b00011000		;4
    .db	0b00011100, 0b00010100		;5
    .db	0b00111100, 0b00010100		;6
    .db	0b00000100, 0b00001100		;7
    .db	0b00111100, 0b00011100		;8
    .db	0b00001100, 0b00011100		;9
    .db	0b00001000, 0b00000000		;-
    .db	0b00110000, 0b00010100		;C
    .db	0b00000000, 0b00000000		;blank
    
    tens_table:
    .db	0b10000011, 0b11000010		;0
    .db	0b10000000, 0b01000000		;1
    .db	0b01000011, 0b11000000		;2
    .db	0b11000010, 0b11000000		;3
    .db	0b11000000, 0b01000010		;4
    .db	0b11000010, 0b10000010		;5
    .db	0b11000011, 0b10000010		;6
    .db	0b10000000, 0b11000000		;7
    .db	0b11000011, 0b11000010		;8
    .db	0b11000000, 0b11000010		;9
    .db	0b01000000, 0b00000000		;-
    .db	0b00000011, 0b10000010		;C
    .db	0b00000000, 0b00000000		;blank
    
    copyright:
    .db	"(C)2007 Silicon Chip Publications P/L",0
    
    
    ;**************************************************************************
    ;**************************************************************************
    ;*
    ;* DEFINE USED SRAM
    ;*
    ;**************************************************************************
    ;**************************************************************************
    
    		.dseg
    
    d_porta:	.byte	1	;port A state save (see flash_display)
    d_portc:	.byte	1	;port C state save
    led_timer:	.byte	1	;led/delay timer (ms)
    sw_code:	.byte	1	;switch input code
    pga_cnt:	.byte	1	;number of pgas on the bus
    pga_ch:		.byte	1	;current channel number
    spi_buff:	.byte	24	;spi tx & rx buffer
    vol_data:	.byte	18	;volume, offset & vflags array
    
    ; Stack top is at $25F (reserve 32 bytes)
    
    ;**************************************************************************
    ;**************************************************************************
    ;*
    ;* DEFINE USED EEPROM
    ;*
    ;**************************************************************************
    ;**************************************************************************
    
    		.eseg
    		.org	0
    
    ee_start:	.byte	1	;reserved
    ee_magic:	.byte	1	;magic number
    ee_blank:	.byte	1	;display blanking select
    ee_ir_sys:	.byte	1	;RC5 equipment address
    ee_ch:		.byte	1	;last selected channel
    ee_vol:		.byte	6*3	;volume, offset & vflags array
    Вот исходник нашел к вашем проекту, времени разобраться пока нет.


    Offтопик:
    Журнал явно для начинающих и тп, вопрос зачем писать гору текста в асме если бы в си/++ это заняло на много раз меньше и понятней.

  18. #137
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Цитата Сообщение от zindi Посмотреть сообщение
    Вот исходник
    Хороший)

  19. #138
    Частый гость
    Автор темы

    Регистрация
    15.04.2009
    Возраст
    35
    Сообщений
    202

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Цитата Сообщение от denz Посмотреть сообщение
    Хороший)
    события какие происходят по полочкам и будет что то делать)

  20. #139
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    А по топологии ПП критических замечаний нет?

  21. #140
    Давно не заходил
    Регистрация
    28.02.2008
    Адрес
    Киев
    Сообщений
    878

    По умолчанию Re: PGA2310/11, как её готовить и как её есть :)

    Вроде уже и к звучанию привык, пока обсуждали

Страница 7 из 13 Первая ... 56789 ... Последняя

Социальные закладки

Социальные закладки

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •