Parcourir la source

AVM: Init project

Vladimir N. Shilov il y a 10 ans
commit
53112dbe1c
6 fichiers modifiés avec 572 ajouts et 0 suppressions
  1. 51 0
      ReadMe.txt
  2. 490 0
      avm.asm
  3. 31 0
      avm.hex
  4. BIN
      avm.lay6
  5. BIN
      avm.spl7
  6. BIN
      model.DSN

+ 51 - 0
ReadMe.txt

@@ -0,0 +1,51 @@
+Если я правильно понял, для oversampling необходимо налиие шумов, поэтому 
+опорное напряжение можно взять от стабилитрона с входных 12В, без всяких 
+конденсаторов. Также можно не увлекатся фильтрацией питания и избавлением от 
+пульсаций, а также отказаться от всяких мер по экранированию измерительных 
+цепей. Также не стоит использовать спящий режим для измерений.
+Уровень шума в измеряемом сигнале, или опорном напряжении должен быть на 
+уровне 1-2LSB, при опорном в 5В -- это 5-10 мВ.
+
+При питании от розетки, можно просто распределить измерения в течении 
+одного периода 100Гц. (нельзя)
+
+Для oversampling нужно произвести 4^n измерений (где n - количество 
+дополнительных бит результата), суммировать результат и разделить на 2^n
+Потом произвести действия по калибровке и можно вычеслять результат.
+На асме ещё нужно принять меры для сохранения дробной части.
+
+Например, 2 доп бита.
+Делаем 16 измерений, суммируем, делим на 4. Дальше как обычно -- умножаем на 
+опорное в миливольтах, и делим на 4096 (ведь результат у нас теперь 12 бит). 
+Получаем результат в миливольтах, с дискретностью опора/4096.
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+2014.10.08
+таки запаял и запустил :-)
+если не учитывать косяк на плате -- то всё удачно. почти.
+ 1. низкая скорость обновления -- нужно увеличивать раз в несколько.
+ 2. яркость бешеная -- нужно увеличить резисторы ом до 200-300. заодно 
+    уменьшиться нагрев стаба. потребление около 100 мА.
+ 3. прыгают показания -- нужно усреднять.
+ 4. подправить значение опорного напряжения под реальное (4.985 В)
+ 5. можно гасить ведущий "0".
+
+
+ - в делителе таймера была ошибка. в итоге частота была ~75 Гц. похоже на правду.
+ - отпочковал версию 2. попробую сделать усреднение по 6-ти измерениям.
+ - измерения будут не каждые 100 мсек, а по-кругу, без лишних ожиданий.
+
+2014.10.09
+можно считать этот проект условно успешно законченым.
+условно в том плане, что оверсэмплинг вроде как тут не работает.
+и всё равно скачет временами цифра в младшем разряде, хотя я применил 
+усреднение по 16-ти значениям.
+а во всём остальном -- всё работает.
+
+ - есть идея попробовать упорядочить измерения и вывод:
+   - измерение начинать после вывода первой цифры;
+   - преобразование результата в и запись в буфер индикатора - после 
+     вывода четвёртой цифры;
+ - возможно это также поможет избавиться от мелтешения цифр.
+ 

+ 490 - 0
avm.asm

@@ -0,0 +1,490 @@
+; Автор:	shilow@ukr.net
+; Дата:		октябрь 2014
+; Название:	avm
+; Версия:	2
+; Имя файла: 	avm.asm, avm.spl7, avm.lay6
+; Для AVR:	ATtiny24A
+; Тактовая частота: 8MHz, внутренний RC-генератор
+; Выполняемые функции: Автомобильный вольтемтр
+; см. ReadMe.txt
+; схема по "Вариант 5"
+
+;******************************
+; инклуды
+.nolist
+.include "tn24Adef.inc"
+.list
+
+;******************************
+; опредления
+.def job0	= r0
+.def job1	= r1
+.def job2	= r2
+.def job3	= r3
+.def LED1	= r4
+.def LED2	= r5
+.def LED3	= r6
+.def LED4	= r7
+; r8
+; r9
+; r10
+; r11
+; r12
+; r13
+.def msrL	= r14	; результат измерений
+.def msrH	= r15
+.def temp	= r16	; рабочая переменная 
+.def tmp1	= r17
+.def cnt1	= r18	; рабочий счётчик
+.def cnt2	= r19	; счётчик циклов АЦП
+.def LEDN	= r20
+; r21
+; r22
+.def bAddr	= r23	; адрес текущей позиции в буфере измерений
+.def bIdx	= r24	; индекс буфера
+.def flags	= r25
+; r26 XL OUT_NIBBL
+; r27 XH
+; r28 YL вывод на ндикатор
+; r29 YH
+; r30 ZL OUT_NIBBL, BIN2BCD
+; r31 ZH
+;******************************
+; константы
+.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 DISP_PORT	= PORTA
+.equ DISP_DDR	= DDRA
+.equ LA1	= 4
+.equ LA2	= 5
+.equ LA3	= 6
+.equ LA4	= 7
+.equ IND_LA	= (1<<LA1)|(1<<LA2)|(1<<LA3)|(1<<LA4)
+.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 VREF	= 500	; опорное напряжение * 10 мВ
+.equ SPH	= 0x3e
+.equ DOT_BIT	= 5	; bit5 -- точка
+.equ BuffSize	= 16	; глубина буфера результатов измерений
+.equ BuffShift	= 4	; сколько раз сдвинуть вправо сумму буфера для усреднения
+
+;******************************
+; макросы
+; сохраняем в стек SREG и R16
+.MACRO PUSHF
+	PUSH	R16
+	IN	R16,SREG
+	PUSH	R16
+.ENDM
+
+; восстанавливаем из стека SREG и R16 
+.MACRO POPF
+	POP	R16
+	OUT	SREG,R16
+	POP	R16
+.ENDM
+
+;******************************
+; ячейки в СОЗУ
+.DSEG
+.ORG	SRAM_START
+;Display:	.byte 4 ; 4 байта для индикатора
+Buffer:		.byte 2*BuffSize
+
+;******************************
+; константы в EEPROM
+;.ESEG
+;smpl1:		.DW	0x0000	; sample 1
+;smpl2:		.DB	0x05	; sample 2
+
+;******************************
+; память программ
+.CSEG
+.ORG 0
+;******************************
+; Таблица векторов прерываний
+	rjmp	RESET		; Reset Handler
+	reti;jmp	INT0		; IRQ0 Handler
+	reti;jmp	PCINT0		; PCINT0 Handler
+	reti;jmp	PCINT1		; PCINT1 Handler
+	reti;jmp	WDT		; Watchdog Interrupt Handler
+	reti;jmp	TIM1_CAPT	; Timer1 Capture Handler
+	reti;jmp	TIM1_COMPA	; Timer1 Compare A Handler
+	reti;jmp	TIM1_COMPB	; Timer1 Compare B Handler
+	rjmp	TIM1_OVF	; Timer1 Overflow Handler
+	reti;jmp	TIM0_COMPA	; Timer0 Compare A Handler
+	reti;jmp	TIM0_COMPB	; Timer0 Compare B Handler
+	reti;jmp	TIM0_OVF	; Timer0 Overflow Handler
+	reti;jmp	ANA_COMP	; Analog Comparator Handler
+	rjmp	ADCC		; ADC Conversion Handler
+;	rjmp	EE_RDY		; EEPROM Ready Handler
+;	rjmp	USI_STR		; USI STart Handler
+;	rjmp	USI_OVF		; USI Overflow Handler
+
+;******************************
+;;;;; Первичная инициализация
+RESET:
+	ldi	temp,high(RAMEND); Main program start
+	out	SPH,temp
+	ldi	temp,low(RAMEND)
+	out	SPL,temp; Set Stack Pointer to top of RAM
+; выкл. аналог, компаратор 
+	ldi	temp,1<<ACD
+	out	ACSR,temp
+; настроим АЦП. опора - Vcc, канал 1, непрерывное преобразование. temp=0b00100010
+	ldi	temp,1<<MUX0
+	out	ADMUX,temp
+	ldi	temp,(1<<ADEN)|(1<<ADSC)|(6<<ADPS0)
+	out	ADCSRA,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,1<<TOIE1
+	out	TIMSK1,temp
+; init
+	ldi	LEDN,0b11101111	; первый индикатор
+	ldi	temp,IND_LA
+	out	DISP_DDR,temp
+	out	DISP_PORT,temp
+	ldi	temp,(1<<SCK)|(1<<DATA)|(1<<LOCK)
+	out	SER_DDR,temp
+	clr	YH
+	ldi	YL,AtLED1	; загружаем адрес 1-го индикатора
+	ldi	temp,0x30
+	mov	LED1,temp
+	mov	LED2,temp
+	mov	LED3,temp
+	mov	LED4,temp
+	ldi	bAddr,low(Buffer)
+	clr	bIdx
+; Enable interrupts
+	sei
+;******************************
+;;;;; Основная программа
+BEGIN:
+;;; запускаем измерения и преобразования
+	clr	msrL
+	clr	msrH		; очистили хранилище результата
+	ldi	cnt2,16		; загрузили счётчик измерений
+	sbi	ADCSRA,ADATE	; запуск free running mode
+	sbi	ADCSRA,ADIE	; разрешаем прерывания от АЦП
+	sbi	ADCSRA,ADSC	; запуск нов. преобразования 
+ML1:
+;	rjmp	PC+1		; nop x 2 -- 1 world 2 takta. или sleep?
+	rcall	WAIT_IRQ
+	tst	cnt2		; гоняем пустой цикл пока будут происходить 16 измерений
+	brne	ML1
+	cbi	ADCSRA,ADIE	; запрещаем прерывания от АЦП
+	cbi	ADCSRA,ADATE	; stop free running mode
+	lsr	msrH
+	ror	msrL
+	lsr	msrH
+	ror	msrL		; поделили результат на 2^2
+	; теперь множим на Vref и делим на 4096
+	ldi	temp,low(VREF)
+	ldi	tmp1,high(VREF)
+	rcall	MPY16U
+	lsr	job1		; выбросили job0 == поделили на 256
+	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 среднее от содержимого буфера.
+; адрес и индекс буфера показывают следующую позицию.
+	
+
+;;; преобразуем msr в десятичный вид и положим в буфер индикатора
+	rcall	bin2BCD16
+	; 4 значащих цифры в job1:job0
+	ldi	XL,AtLED1
+	clr	XH
+	mov	temp,job1
+	swap	temp
+	; проверим на ведущий 0
+	andi	temp,0x0F	; отсекаем ст ниббл
+	tst	temp
+	brne	MakeDigit3
+	ldi	temp,0xFF	; код пустого индикатора
+	st	X+,temp
+	rjmp	MakeDigit2
+MakeDigit3:
+	rcall	OUT_NIBBL	; ниббл 3, десятки
+MakeDigit2:
+	mov	temp,job1
+	sbr	flags,1<<NEED_DOT
+	rcall	OUT_NIBBL	; ниббл 2, единицы
+	cbr	flags,1<<NEED_DOT
+	mov	temp,job0
+	swap	temp
+	rcall	OUT_NIBBL	; ниббл 1, десятые
+	mov	temp,job0
+	rcall	OUT_NIBBL	; ниббл 0, сотые
+
+;;; Конец цикла
+	rjmp	BEGIN
+
+;******************************
+;;;;; Подпрограммы
+
+;******************************
+; делать нечего - поспим... ждём любое прерывание
+WAIT_IRQ:
+	ldi	temp,1<<SE
+	out	MCUCR,temp	; простой режим сна
+	sleep			; спим.
+	clr	temp
+	out	MCUCR,temp	; со сна
+	ret
+
+;******************************
+; выдаёт в порт 8 бит из temp, портит cnt1
+LEDOUT:
+	ldi	cnt1,8		; счетчик бит
+LO1:	cbi	SER_PORT,SCK	; SCK=0
+	lsl	temp		; старший бит в перенос
+	brcc	LO0		; если в переносе 0 — перейти
+	sbi	SER_PORT,DATA	; выдали 1
+	rjmp	LOE
+LO0:	cbi	SER_PORT,DATA	; выдали 0
+LOE:	sbi	SER_PORT,SCK	; SCK=1, сдвиг данных
+	dec	cnt1
+	brne	LO1
+	ret
+
+;******************************
+;** переводим младший ниббл temp в код для 7-сгм инд и кладём его в озу по адр X
+OUT_NIBBL:
+	ldi	ZL,low(LEDnd*2)
+	ldi	ZH,high(LEDnd*2); адрес кодов для индикатора
+	andi	temp,0x0F	; отсекаем ст ниббл. получаем смещение
+	add	ZL,temp
+	adc	ZH,XH		; добавили смещение
+	lpm	temp,Z		; прочитали код
+	sbrc	flags,NEED_DOT	; нужна точка?
+	cbr	temp,1<<DOT_BIT	; включаем точку
+	st	X+,temp		; записали код в позицию индикатора
+	ret
+
+;******************************
+; перемножение двух 16-разрядных величин, результат 3 байта, рабочая
+; множимое msrH:msrL, множитель tmp1:temp, результат job2:job1:job0
+; использует cnt1
+MPY16U:
+	clr	job2		;clear 1 highest bytes of result
+	ldi	cnt1,16		;init loop counter
+m16u_1:	lsr	tmp1
+	ror	temp
+	brcc	noad8		;if bit 0 of multiplier set
+	add	job1,msrL	;add multiplicand Low to byte 1 of res
+	adc	job2,msrH	;add multiplicand high to byte 2 of res
+noad8:	ror	job2		;shift right result byte 2
+	ror	job1		;rotate right result byte 1
+	ror	job0		;rotate result byte 0 and multiplier Low
+	dec	cnt1		;decrement loop counter
+	brne	m16u_1		;if not done, loop more
+	ret
+
+;******************************
+;* This subroutine converts a 16-bit number (msrH:msrL) to a 5-digit 
+;* packed BCD number represented by 3 bytes (job2:job1:job0).
+;* MSD of the 5-digit number is placed in the lowermost nibble of job2.
+bin2BCD16:
+	ldi	cnt1,16		;Init loop counter
+	clr	job2		;clear result (3 bytes)
+	clr	job1
+	clr	job0
+	clr	ZH		;clear ZH
+bBCDx_1:lsl	msrL		;shift input value
+	rol	msrH		;through all bytes
+	rol	job0
+	rol	job1
+	rol	job2
+	dec	cnt1		;decrement loop counter
+	brne	bBCDx_2		;if counter not zero
+	ret			;   return
+bBCDx_2:ldi	ZL,AtBCD2+1	;Z points to result MSB + 1
+bBCDx_3:ld	temp,-Z		;get (Z) with pre-decrement
+	subi	temp,-$03	;add 0x03
+	sbrc	temp,3		;if bit 3 not clear
+	st	Z,temp		;	store back
+	ld	temp,Z		;get (Z)
+	subi	temp,-$30	;add 0x30
+	sbrc	temp,7		;if bit 7 not clear
+	st	Z,temp		;	store back
+	cpi	ZL,AtBCD0	;done all three?
+	brne	bBCDx_3		;loop again if not
+	rjmp	bBCDx_1
+
+
+;******************************
+;;;;; Обработчики прерываний
+
+;******************************
+; Timer1 Overflow Handler
+TIM1_OVF:
+; выводим данные на 7-ми сегментные индикаторы
+; отсчитываем 100 мсек интервал для запуска измерений
+	PUSHF
+
+T1L1:
+	;***************************
+	; вывод на индикатор очередной цифры
+	in	temp,DISP_PORT
+	ori	temp,IND_LA	; изменяем только нужные пины
+	out	DISP_PORT,temp	; погасили все индикаторы
+	cbi	SER_PORT,LOCK	; подгтовили защёлку
+	ld	temp,Y+		; в темп - текущая цифра, адрес=+1
+	rcall	LEDOUT		; выпихнули очередной байт в регистр
+	sbi	SER_PORT,LOCK	; из 0 в 1 -- защёлкивание данных в регистре
+	in	temp,DISP_PORT
+	ori	temp,IND_LA
+	and	temp,LEDN
+	out	DISP_PORT,temp	; включаем очередной индикатор
+	lsl	LEDN		; сдвинули - следующий индикатор
+	brlo	T1L2		; вышли за границы? проверка 
+	ldi	LEDN,0b11101111	; да, загружаем начальное значение
+	ldi	YL,AtLED1	; загружаем адрес 1-го индикатора
+T1L2:
+	ldi	temp,T1DIVH
+	out	TCNT1H,temp
+	ldi	temp,T1DIVL
+	out	TCNT1L,temp	; перезапустили таймер 1
+
+	POPF
+	reti
+
+
+;***************************
+; ADC Conversion Handler
+; суммируем результаты измерений, уменьшаем счётчик.
+ADCC:
+	in	temp,ADCL
+	add	msrL,temp
+	in	temp,ADCH
+	adc	msrH,temp
+	dec	cnt2
+	reti
+
+;***************************
+; байты для вывода 0-9 на индикаторы с ОА, без точки. точка вкл установкой бита 5
+LEDnd:	.DB	0x30,0xF3,0x2A,0xA2,0xE1,0xA4,0x24,0xF2,0x20,0xA0
+
+.exit
+
+INT0:		; IRQ0 Handler
+	reti
+
+PCINT0:		; PCINT0 Handler
+	reti
+
+PCINT1:		; PCINT1 Handler
+	reti
+
+WDT:		; Watchdog Interrupt Handler
+	reti
+
+TIM1_CAPT:	; Timer1 Capture Handler
+	reti
+
+TIM1_COMPA:	; Timer1 Compare A Handler
+	reti
+
+TIM1_COMPB:	; Timer1 Compare B Handler
+	reti
+
+TIM0_COMPA:	; Timer0 Compare A Handler
+	reti
+
+TIM0_COMPB:	; Timer0 Compare B Handler
+	reti
+
+TIM0_OVF:	; Timer0 Overflow Handler
+	reti
+
+ANA_COMP:	; Analog Comparator Handler
+	reti
+
+EE_RDY:		; EEPROM Ready Handler
+	reti
+
+USI_STR:	; USI STart Handler
+	reti
+
+USI_OVF:	; USI Overflow Handler
+	reti
+
+;
+.exit

+ 31 - 0
avm.hex

@@ -0,0 +1,31 @@
+:020000020000FC
+:100000000DC0189518951895189518951895189568
+:10001000B3C01895189518951895C8C000E00EBF84
+:100020000FED0DBF00E808B901E007B906EC06B90D
+:1000300003E00EBD0DEF0DBD0FE80CBD01E00CB9E6
+:100040004FEE00EF0ABB0BBB07E007BBDD27C4E0A8
+:1000500000E3402E502E602E702E70E68827789494
+:10006000EE24FF2430E1359A339A369A44D0332374
+:10007000E9F733983598F694E794F694E79404EF0B
+:1000800011E054D01694079416940794A72FBB2719
+:100090000D921C921197EE24FF2420E10D91E00EA9
+:1000A0000D91F01E8395803111F4A0E688272A95E2
+:1000B000A9F712968395803111F4A0E688277A2F4C
+:1000C00024E0F694E7942A95E1F73DD0A4E0BB271D
+:1000D000012D02950F70002319F40FEF0D9301C04D
+:1000E0001BD0012D906418D09F7B002D029514D059
+:1000F000002D12D0B5CF00E205BF8895002705BFBF
+:10010000089528E0C098000F10F4C19A01C0C1986A
+:10011000C09A2A95B9F70895E8EBF1E00F70E00F67
+:10012000FB1F049196FD0F7D0D930895222420E17D
+:100130001695079510F41E0C2F1C279417940794FE
+:100140002A95B1F7089520E1222411240024FF27E5
+:10015000EE0CFF1C001C111C221C2A9509F40895AA
+:10016000E3E002910D5F03FD00830081005D07FD68
+:100170000083E030B1F7ECCF0F930FB70F930BB3C1
+:10018000006F0BBBC2980991BCDFC29A0BB3006F22
+:1001900004230BBB440F10F04FEEC4E00DEF0DBD78
+:1001A0000FE80CBD0F910FBF0F91189504B1E00E31
+:1001B00005B1F01E3A95189530F32AA2E1A424F275
+:0201C00020A07D
+:00000001FF

BIN
avm.lay6


BIN
avm.spl7


BIN
model.DSN