ソースを参照

AVM: 4-ая версия.

Vladimir N. Shilov 9 年 前
コミット
068a0a5998
5 ファイル変更156 行追加206 行削除
  1. 24 46
      ReadMe.txt
  2. 107 133
      avm.asm
  3. 25 27
      avm.hex
  4. BIN
      avm.spl7
  5. BIN
      model.pdsprj

+ 24 - 46
ReadMe.txt

@@ -1,46 +1,24 @@
-2014.10.09
-Появились идеи для третьей версии.
-Это уже будет не авто-вольт-метр, а ups-вольт-метр:
- - основная задача -- контроль напряжения аккума в упсе.
- - раз в секунду выводить текущее напряжение через програмный UART
- - если на PA0 низкий уровень (опционально), то:
-   - при напряжении аккума ниже 10.02 вольта отключать индикацию,
-   - при восстановлении напрчжения -- включать обратно
-   - при напряжении аккума ниже 9.? вольт -- уходить в глубокий даун.
-
- - есть идея попробовать упорядочить измерения и вывод:
-   - измерение начинать после вывода первой цифры;
-   - преобразование результата в и запись в буфер индикатора - после 
-     вывода четвёртой цифры;
- - возможно это также поможет избавиться от мельтешения цифр.
-
----
-2014.10.10 
-Если верить протеусу -- основной цикл проходит за 1.83 мсек, цикл BCD 
-занимает 106 мксек, цикл усреднения 32 мксек, а сам цикл измерений -- 
-порядка 1692 мксек.
-Никакого смысла в таком частом измерении нет -- вставил sleep в конце 
-цикла, теперь основной цикл начинается после вывода очередной цифры, т.е. 
-каждые 5 мсек.
-
-поменял таймер 1 на 0, цикл уменьшил до 4 мсек (250 Гц),
-прошивка -- 440 байт, из них 10 байт данных, ну и 32 байта в ОЗУ.
-люблю асм :-)
-
-тест в железе прошел успешно.
-
-попробую я этот вольтметр всабачить в прикуриватель и всё-таки 
-использовать в машине. а для упс-а сделать отдельный девайс...
-
-сегментные резисторы заменил на 200 ом -- разницы в яркости не заметно.
-ток потреббления не мерял.
-
----
-2014.10.11
-
-калибровал этот вольтметр по китайскому 5-ти разрядному на MCP3421
-по итогам:
- - выкинул стабилитрон на 5.1В по входу -- начиная с 10 волт он заваливал 
-   входное напряжение
- - исправил установку напряжения питания (опорное) на 4.98 вольта
- - поставил супресор на входе схемы.
+2015.06.04
+
+Четвёртая версия.
+Хочу добить, чтобы окончательно избавиться от мельтешения последних разрядов.
+Идея такая:
+ - АЦП на минимальную скорость
+ - мерять один раз после обновления индикатора и накапливать результат
+ - через 64 измерения посчитать результат и вывести на индикатор
+ - опроное напряжение перевести в миливольты.
+ - на МК напаять тантал на 100 мкф 6.3 вольта
+ - если частоту отображения сделать 200 Гц, то обновление инфы будет чуть чаще 
+   чем 3 раза в сек (3.125 Гц), или можно сдеть 256 Гц, для обновления
+   4 раза в секунду.
+ - на 8-ми битном таймере ровных 200 или 256 Гц не сделать.
+
+В конце основного цикла спим и ждём любой прерывание.
+По выводу очердной цифры нужно ставить флаг, по которому запускать очередное 
+измерение.
+После 64-х измерений пересчитываем результат.
+Итого, каждое значение будет отображаться 16 циклов.
+Минимальная частота АЦП -- 50 кГц, при 8 МГц тактовой можно сделать 62.5 кГц.
+
+Вроде получилось, нудно проверять.
+392 байта, и 10 байт данных.

+ 107 - 133
avm.asm

@@ -1,13 +1,12 @@
 ; Автор:	shilow@ukr.net
-; Дата:		октябрь 2014
+; Дата:		июнь 2015
 ; Название:	avm
-; Версия:	3
+; Версия:	4
 ; Имя файла: 	avm.asm, avm.spl7, avm.lay6
 ; Для AVR:	ATtiny24A
 ; Тактовая частота: 8MHz, внутренний RC-генератор
 ; Выполняемые функции: Автомобильный вольтемтр
 ; см. ReadMe.txt
-; схема по "Вариант 5"
 
 ;******************************
 ; инклуды
@@ -40,8 +39,8 @@
 .def LEDN	= r20
 ; r21
 ; r22
-.def bAddr	= r23	; адрес текущей позиции в буфере измерений
-.def bIdx	= r24	; индекс буфера
+;.def = r23	; 
+;.def = r24	; 
 .def flags	= r25
 ; r26 XL OUT_NIBBL
 ; r27 XH
@@ -54,8 +53,8 @@
 .equ AtBCD0	= 0	;address of job0
 .equ AtBCD2	= 2	;address of job2
 .equ AtLED1	= 4	;address of LED1
-.equ MSEK100	= 7	; 7bit of flags == 0.1 sek
-.equ NEED_DOT	= 6	; нужна точка
+.equ MEASURE	= 7	; 7bit of flags -- пора мерять
+.equ NEED_DOT	= 6	; 6bit of flags -- нужна точка
 .equ DISP_PORT	= PORTA
 .equ DISP_DDR	= DDRA
 .equ LA1	= 4
@@ -63,18 +62,18 @@
 .equ LA3	= 6
 .equ LA4	= 7
 .equ IND_LA	= (1<<LA1)|(1<<LA2)|(1<<LA3)|(1<<LA4)
+.equ FIRST_LED	= 0b11101111
 .equ SER_PORT	= PORTB
 .equ SER_DDR	= DDRB
 .equ SCK	= 0
 .equ DATA	= 1
 .equ LOCK	= 2
-;.equ T1DIVL	= 0x8F
-;.equ T1DIVH	= 0xFD	; 0x10000-8MHz/64/200Hz
-.equ T0DIV	= 0x83	; 0x100-8MHz/256/250Hz
-.equ VREF	= 498	; опорное напряжение, десятки миливольт
+.equ T1DIVL	= 0x8F
+.equ T1DIVH	= 0xFD	; 0x10000-8MHz/64/200Hz
+;.equ T0DIV	= 0x83	; 0x100-8MHz/256/250Hz
+.equ VREF	= 4980	; опорное напряжение, миливольты
 .equ SPH	= 0x3e
 .equ DOT_BIT	= 5	; bit5 -- точка
-.equ BuffSize	= 16	; глубина буфера результатов измерений
 .equ BuffShift	= 4	; сколько раз сдвинуть вправо сумму буфера для усреднения
 
 ;******************************
@@ -95,10 +94,10 @@
 
 ;******************************
 ; ячейки в СОЗУ
-.DSEG
-.ORG	SRAM_START
+;.DSEG
+;.ORG	SRAM_START
 ;Display:	.byte 4 ; 4 байта для индикатора
-Buffer:		.byte 2*BuffSize
+;Buffer:		.byte 2*BuffSize
 
 ;******************************
 ; константы в EEPROM
@@ -120,10 +119,10 @@ Buffer:		.byte 2*BuffSize
 	reti;jmp	TIM1_CAPT	; Timer1 Capture Handler
 	reti;jmp	TIM1_COMPA	; Timer1 Compare A Handler
 	reti;jmp	TIM1_COMPB	; Timer1 Compare B Handler
-	reti;jmp	TIM1_OVF	; Timer1 Overflow Handler
+	rjmp	TIM1_OVF	; Timer1 Overflow Handler
 	reti;jmp	TIM0_COMPA	; Timer0 Compare A Handler
 	reti;jmp	TIM0_COMPB	; Timer0 Compare B Handler
-	rjmp	TIM0_OVF	; Timer0 Overflow Handler
+	reti;rjmp	TIM0_OVF	; Timer0 Overflow Handler
 	reti;jmp	ANA_COMP	; Analog Comparator Handler
 	rjmp	ADCC		; ADC Conversion Handler
 ;	rjmp	EE_RDY		; EEPROM Ready Handler
@@ -142,34 +141,36 @@ RESET:
 	ldi	temp,1<<ACD
 	out	ACSR,temp
 
-;;; настроим АЦП. опора - Vcc, канал 1, непрерывное преобразование. temp=0b00100010
+;;; настроим АЦП. опора - Vcc, канал 1, 62.5 kHz
 	ldi	temp,1<<MUX0
 	out	ADMUX,temp
-	ldi	temp,(1<<ADEN)|(1<<ADSC)|(6<<ADPS0)
+	ldi	temp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
 	out	ADCSRA,temp
+	ldi	temp,(1<<ADC1D)
+	out	DIDR0,temp
 
 ;;; таймер0 интервалы в 4 мсек (250 Hz). прескалер = 256
-	ldi	temp,(1<<CS02)
-	out	TCCR0B,temp
-	ldi	temp,T0DIV
-	out	TCNT0,temp	; запустили таймер
+;	ldi	temp,(1<<CS02)
+;	out	TCCR0B,temp
+;	ldi	temp,T0DIV
+;	out	TCNT0,temp	; запустили таймер
 ; прерывания от таймеров
-	ldi	temp,(1<<TOIE0)
-	out	TIMSK0,temp	; разрешим прерывание по переполнению
+;	ldi	temp,(1<<TOIE0)
+;	out	TIMSK0,temp	; разрешим прерывание по переполнению
 
 ;; timer1 - частота 200 гц, при тактовой 8МГц, прескалер=64
-;	ldi	temp,(3<<CS10)
-;	out	TCCR1B,temp
-;	ldi	temp,T1DIVH
-;	out	TCNT1H,temp
-;	ldi	temp,T1DIVL
-;	out	TCNT1L,temp	; запустили таймер 1
+	ldi	temp,(3<<CS10)
+	out	TCCR1B,temp
+	ldi	temp,T1DIVH
+	out	TCNT1H,temp
+	ldi	temp,T1DIVL
+	out	TCNT1L,temp	; запустили таймер 1
 ; разрешаем прерывание от таймера
-;	ldi	temp,1<<TOIE1
-;	out	TIMSK1,temp
+	ldi	temp,1<<TOIE1
+	out	TIMSK1,temp
 
 ;;; init
-	ldi	LEDN,0b11101111	; первый индикатор
+	ldi	LEDN,FIRST_LED	; первый индикатор
 	ldi	temp,IND_LA
 	out	DISP_DDR,temp
 	out	DISP_PORT,temp
@@ -182,114 +183,79 @@ RESET:
 	mov	LED2,temp
 	mov	LED3,temp
 	mov	LED4,temp
-	ldi	bAddr,low(Buffer)
-	clr	bIdx
 
 ;;; Enable interrupts
 	sei
 ;******************************
 ;;;;; Основная программа
 BEGIN:
-;;; запускаем измерения и преобразования
+
+;;; Подготовимся к новым измерениям
 	clr	msrL
 	clr	msrH		; очистили хранилище результата
-	ldi	cnt1,16		; загрузили счётчик измерений
-	sbi	ADCSRA,ADATE	; запуск free running mode
+	ldi	cnt1,64		; загрузили счётчик измерений
 	sbi	ADCSRA,ADIE	; разрешаем прерывания от АЦП
+
+;;; Конец цикла, в начале :-)
+MAIN_END:
+	rcall	WAIT_IRQ
+
+;;; Если при выходе из прерывания стоит флаг -- запускаем измерения
+	sbrs	flags,MEASURE	; пора мерять?
+	rjmp	CHECK_MEASURE	; если нет -- идём дальше
+	cbr	flags,1<<MEASURE ; сбросим флаг
+
+;;; запускаем новый цикл измерений
+START_MEASURE:
 	sbi	ADCSRA,ADSC	; запуск нов. преобразования 
+	rjmp	MAIN_END	; и спим дальше
 
-ML1:
-;	rjmp	PC+1		; nop x 2 -- 1 world 2 takta. или sleep?
-	rcall	WAIT_IRQ	; один цикл измерения, ЕМНИП больше 100 мкс...
-	tst	cnt1		; гоняем пустой цикл пока будут происходить 16 измерений
+;;; Если нет флага, значит прерывание было от АЦП
+CHECK_MEASURE:
+	tst	cnt1		; закончили измерения?
+	brne	MAIN_END	; если нет -- спим дальше
+
+;; закончили, отключим прерывания
+	cbi	ADCSRA,ADIE
 
-	brne	ML1
-	cbi	ADCSRA,ADIE	; запрещаем прерывания от АЦП
-	cbi	ADCSRA,ADATE	; stop free running mode
+;;; Здесь происходят все преобразования
+ML1:
+;; у нас 64 измерения, оверсэмплинг. пока делим на 8
 	lsr	msrH
 	ror	msrL
 	lsr	msrH
-	ror	msrL		; поделили результат на 2^2
-	; теперь множим на Vref и делим на 4096
+	ror	msrL
+	lsr	msrH
+	ror	msrL		; поделили результат на 2^3
+
+;; теперь нужно умножить на Vref и поделить на 8192 -- получим миливольты на входе АЦП
+;; а затем умножить на 4, чтобы получить напряжение на входе делителя.
+;; т.е. делим на 2048
 	ldi	temp,low(VREF)
 	ldi	tmp1,high(VREF)
-	rcall	MPY16U
-	lsr	job1		; выбросили job0 == поделили на 256
+	rcall	MPY16U		; отбросили job0 == поделили на 256
+	lsr	job1
+	ror	job0
+	lsr	job1
 	ror	job0
 	lsr	job1
 	ror	job0
-;;	lsr	job1
-;;	ror	job0
-;;	lsr	job1
-;;	ror	job0		; доделили на 16, итого поделили на 4096
-;	ldi	cnt1,4
-;ML2:	lsr	job2
-;	ror	job1
-;	dec	cnt1
-;	brne	ML2
-;	mov	msrL,job0
-;	mov	msrH,job1	; в MSR - напряжение в 10мВ
-;!!! умножить на входной делитель
-;; -- не доделили на 4 == умножили теперь на 4.
+
 ;; в реале нужно будет множить например на 400 и делить на 100 -- под реальный делитель.
 ; тут можно добавить коррекцию усиления.
 
-; сейчас текущий результат измерний в job1:job0
-
-
-;;; усреднения
-	; сохраним текущий результат
-	mov	XL,bAddr
-	clr	XH
-	st	X+,job0
-	st	X,job1		; сохранили
-	sbiw	X,1		; буфер в старой позиции -- на текущем результате
-	clr	msrL
-	clr	msrH
-
-	; теперь как-то нужно просуммировать содержимое буфера
-	ldi	cnt1,BuffSize
-BuffSummarize:
-	ld	temp,X+
-	add	msrL,temp
-	ld	temp,X+
-	adc	msrH,temp
-	inc	bIdx
-	cpi	bIdx,BuffSize
-	brne	BuffChkCnt
-	ldi	XL,low(Buffer)
-	clr	bIdx
-BuffChkCnt:
-	dec	cnt1
-	brne	BuffSummarize
-
-	adiw	X,2		; адрес буфера - на следующую ячейку
-	inc	bIdx
-	cpi	bIdx,BuffSize
-	brne	BuffAddrSave
-	ldi	XL,low(Buffer)
-	clr	bIdx
-BuffAddrSave:
-	mov	bAddr,XL	; сохранили текущий адрес буфера
-
-	; тут имеем в msr сумму всех элементов массива. делим:
-	ldi	cnt1,BuffShift
-BuffDivide:
-	lsr	msrH
-	ror	msrL
-	dec	cnt1
-	brne	BuffDivide
-; теперь в msr среднее от содержимого буфера.
-; адрес и индекс буфера показывают следующую позицию.
-	
+; сейчас текущий результат измерний в job1:job0. Перенесём в msr
+	mov	msrH,job1
+	mov	msrL,job0
+; наш результат, это по сути среднее за 64 измерения, в миливольтах, по входу.
 
 ;;; преобразуем msr в десятичный вид и положим в буфер индикатора
 	rcall	bin2BCD16
-	; 4 значащих цифры в job1:job0
+	; 5 значащих цифр в job2:job1:job0
 	ldi	XL,AtLED1
 	clr	XH
-	mov	temp,job1
-	swap	temp
+;; первый, старший разряд -- десятки вольт, младший нибл job2
+	mov	temp,job2
 	; проверим на ведущий 0
 	andi	temp,0x0F	; отсекаем ст ниббл
 	tst	temp
@@ -299,21 +265,25 @@ BuffDivide:
 	rjmp	MakeDigit2
 MakeDigit3:
 	rcall	OUT_NIBBL	; ниббл 3, десятки
+;; второй разряд -- единицы вольт, старший нибл job1
 MakeDigit2:
 	mov	temp,job1
+	swap	temp
 	sbr	flags,1<<NEED_DOT
-	rcall	OUT_NIBBL	; ниббл 2, единицы
+	rcall	OUT_NIBBL
 	cbr	flags,1<<NEED_DOT
+;; третий разряд -- десятые вольта, младший нибл job1
+	mov	temp,job1
+	rcall	OUT_NIBBL
+;; четвёртый разряд -- сотые вольта, старший нибл job0
 	mov	temp,job0
 	swap	temp
-	rcall	OUT_NIBBL	; ниббл 1, десятые
-	mov	temp,job0
-	rcall	OUT_NIBBL	; ниббл 0, сотые
+	rcall	OUT_NIBBL
 
-;;; Конец цикла
-	rcall	WAIT_IRQ
+;;; и на новый виток
 	rjmp	BEGIN
 
+
 ;******************************
 ;;;;; Подпрограммы
 
@@ -396,13 +366,15 @@ bBCDx_3:ld	temp,-Z		;get (Z) with pre-decrement
 ;;;;; Обработчики прерываний
 
 ;******************************
-; Timer0 Overflow Handler
-TIM0_OVF:
+; Timer1 Overflow Handler
+TIM1_OVF:
 ; выводим данные на 7-ми сегментные индикаторы
 	PUSHF
 
-	ldi	temp,T0DIV
-	out	TCNT0,temp	; перезапустили таймер 0
+	ldi	temp,T1DIVH
+	out	TCNT1H,temp
+	ldi	temp,T1DIVL
+	out	TCNT1L,temp	; перезапустили таймер 1
 
 	;***************************
 	; вывод на индикатор очередной цифры
@@ -414,15 +386,15 @@ TIM0_OVF:
 
 ;;; выпихнем очередной байт в регистр
 	ldi	cnt2,8		; счетчик бит
-LO1:	cbi	SER_PORT,SCK	; SCK=0
+TLO1:	cbi	SER_PORT,SCK	; SCK=0
 	lsl	temp		; старший бит в перенос
-	brcc	LO0		; если в переносе 0 — перейти
+	brcc	TLO0		; если в переносе 0 — перейти
 	sbi	SER_PORT,DATA	; выдали 1
-	rjmp	LOE
-LO0:	cbi	SER_PORT,DATA	; выдали 0
-LOE:	sbi	SER_PORT,SCK	; SCK=1, сдвиг данных
+	rjmp	TLOE
+TLO0:	cbi	SER_PORT,DATA	; выдали 0
+TLOE:	sbi	SER_PORT,SCK	; SCK=1, сдвиг данных
 	dec	cnt2
-	brne	LO1
+	brne	TLO1
 
 	sbi	SER_PORT,LOCK	; из 0 в 1 -- защёлкивание данных в регистре
 	in	temp,DISP_PORT
@@ -430,11 +402,13 @@ LOE:	sbi	SER_PORT,SCK	; SCK=1, 
 	and	temp,LEDN
 	out	DISP_PORT,temp	; включаем очередной индикатор
 	lsl	LEDN		; сдвинули - следующий индикатор
-	brlo	T0L2		; вышли за границы? проверка 
-	ldi	LEDN,0b11101111	; да, загружаем начальное значение
+	brlo	T1L2		; вышли за границы? проверка 
+	ldi	LEDN,FIRST_LED	; да, загружаем начальное значение
 	ldi	YL,AtLED1	; загружаем адрес 1-го индикатора
 
-T0L2:
+T1L2:
+	sbr	flags,1<<MEASURE ; пора мерять
+	;;; выход
 	POPF
 	reti
 

+ 25 - 27
avm.hex

@@ -1,30 +1,28 @@
 :020000020000FC
 :100000000DC0189518951895189518951895189568
-:10001000189518951895A4C01895C3C000E00EBF98
-:100020000FED0DBF00E808B901E007B906EC06B90D
-:1000300004E003BF03E802BF01E009BF4FEE00EF99
-:100040000ABB0BBB07E007BBDD27C4E000E3402E83
-:10005000502E602E702E70E688277894EE24FF24B0
-:1000600020E1359A339A369A45D02223E9F733981E
-:100070003598F694E794F694E79402EF11E04AD0AD
-:100080001694079416940794A72FBB270D921C92E1
-:100090001197EE24FF2420E10D91E00E0D91F01E4A
-:1000A0008395803111F4A0E688272A95A9F7129646
-:1000B0008395803111F4A0E688277A2F24E0F69406
-:1000C000E7942A95E1F733D0A4E0BB27012D0295F0
-:1000D0000F70002319F40FEF0D9301C011D0012D03
-:1000E00090640ED09F7B002D02950AD0002D08D081
-:1000F00001D0B4CF00E205BF8895002705BF089561
-:10010000EEEAF1E00F70E00FFB1F049196FD0F7D0A
-:100110000D930895222420E11695079510F41E0CE6
-:100120002F1C2794179407942A95B1F7089520E17E
-:10013000222411240024FF27EE0CFF1C001C111C9C
-:10014000221C2A9509F40895E3E002910D5F03FD56
-:1001500000830081005D07FD0083E030B1F7ECCF44
-:100160000F930FB70F9303E802BF0BB3006F0BBBE6
-:10017000C298099138E0C098000F10F4C19A01C0EC
-:10018000C198C09A3A95B9F7C29A0BB3006F04238D
-:100190000BBB440F10F04FEEC4E00F910FBF0F9157
-:1001A000189504B1E00E05B1F01E2A95189530F3AC
-:0801B0002AA2E1A424F220A020
+:1000100091C01895189518951895B0C000E00EBFBE
+:100020000FED0DBF00E808B901E007B907EC06B90C
+:1000300002E001B903E00EBD0DEF0DBD0FE80CBDF0
+:1000400001E00CB94FEE00EF0ABB0BBB07E007BBAA
+:10005000DD27C4E000E3402E502E602E702E7894F1
+:10006000EE24FF2420E4339A2FD097FF03C09F771C
+:10007000369AFACF2223C1F73398F694E794F69490
+:10008000E794F694E79404E713E12ED016940794CE
+:100090001694079416940794F12CE02C32D0A4E027
+:1000A000BB27022D0F70002319F40FEF0D9301C031
+:1000B00011D0012D029590640DD09F7B012D0AD0A7
+:1000C000002D029507D0CCCF00E205BF8895002710
+:1000D00005BF0895E8E8F1E00F70E00FFB1F049101
+:1000E00096FD0F7D0D930895222420E11695079526
+:1000F00010F41E0C2F1C2794179407942A95B1F71F
+:10010000089520E1222411240024FF27EE0CFF1C77
+:10011000001C111C221C2A9509F40895E3E00291A9
+:100120000D5F03FD00830081005D07FD0083E0306B
+:10013000B1F7ECCF0F930FB70F930DEF0DBD0FE895
+:100140000CBD0BB3006F0BBBC298099138E0C0988F
+:10015000000F10F4C19A01C0C198C09A3A95B9F73E
+:10016000C29A0BB3006F04230BBB440F10F04FEE89
+:10017000C4E090680F910FBF0F91189504B1E00E85
+:1001800005B1F01E2A95189530F32AA2E1A424F2B5
+:0201900020A0AD
 :00000001FF

BIN
avm.spl7


BIN
model.pdsprj