Ассемблер (ASM) для AVR в вопросах и ответах

Обсуждаем контроллеры компании Atmel.
Аватара пользователя
dr.doc
Это не хвост, это антенна
Сообщения: 1368
Зарегистрирован: Вс мар 28, 2010 12:52:22
Откуда: Беларусь

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение dr.doc »

Термометр в proteus работает и именно со 2-м и 0-м байтами. В спойлере участок кода приемника. Частота - 8 МГц.
Спойлерreceive:
/*
Операция чтения бита: Вывод микроконтроллера устанавливается
в режим выхода и на нем устанавливается логический ноль.
Выдерживается определенная пауза, вывод переводится в режим
входа в состоянии Hi-z, выдерживается пауза, а затем микроконтроллер
считывает потенциал вывода.
*/
ldi r19,8 ; Count bit
ldi r18,9 ; Count byte
clr r16 ; Очистим регистр приемника
receive_in:
; Выдадим старт-бит
rcall init_out
cbi Dataport,DATA ; Clear pin
rcall Delay_3uS ; Start slot
rcall init_in ; Вывод на вход
rcall Delay_20uS ; Прочитаем значение бита:
clc ; Clear Carry
sbic Datapin,DATA ; Пропустим команду при лог. 0
sec ; Установим Carry в 1
ror r16 ; Carry в бит 7

rcall Delay_60uS ; Ждем окончания слота времени

dec r19
brne receive_in ; next bit
; Байт прочитан. Запишем его в ячейку
cpi r18,9
brne _8_1
sts rec+0,r16
_8_1:
cpi r18,8
brne _7_1
sts rec+1,r16
_7_1:
cpi r18,7
brne _6_1
sts rec+2,r16
_6_1:
cpi r18,6
brne _5_1
sts rec+3,r16
_5_1:
cpi r18,5
brne _4_1
sts rec+4,r16
_4_1:
cpi r18,4
brne _3_1
sts rec+5,r16
_3_1:
cpi r18,3
brne _2_1
sts rec+6,r16
_2_1:
cpi r18,2
brne _1_1
sts rec+7,r16
_1_1:
cpi r18,1
brne _0_1
sts rec+8,r16
_0_1:
clr r16 ; Очистим приемник

dec r18
cpi r18,0
brne receive_in
ret
Вот - дальнейшая обработка:
Спойлер; Для корректного отображения нужно поделить значение на 16
; Но, сначала определимся со знаком

lds r16,rec+0
lds r17,rec+2
sbrs r17,6
rjmp positiv
Появится время - проверю код в железе и отпишусь. Пока нет индикатора, поэтому придется передать данные на комп...
«Еще я хотел бы, чтобы наши ученые изобрели какой-то новый источник энергии, чтобы мы на коленях не ползали даже перед нашими братьями, умоляя их и выпрашивая тонну нефти или кубометр газа», — рассказал белорусский президент.
Реклама
Аватара пользователя
Gudd-Head
Друг Кота
Сообщения: 20092
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение Gudd-Head »

dr.doc писал(а):Пока нет индикатора
Тут камрад как-то давно (может даже в статьях) выкладывал код (?) с выводом трёх (?) значащих цифр (напряжение свинцового аккума вроде) на один светодиод :idea:
Количество длинных миганий — десятки, мигания покороче — единицы и совсем короткие — десятые доли вольта. :)
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Реклама
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение Alexeyslav »

Тогдауж, морзянкой... так можно тексты целые выводить.
Аватара пользователя
Gudd-Head
Друг Кота
Сообщения: 20092
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение Gudd-Head »

Alexeyslav писал(а):Тогдауж, морзянкой...
Это для совсем тру радиолюбителей :)))
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
Jack_A
Друг Кота
Сообщения: 6307
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение Jack_A »

Alexeyslav писал(а):Тогдауж, морзянкой... так можно тексты целые выводить.
Причем на слух :) "ЗА ЗАЙ-ца-ми" - так на слух нас учили принимать букву 'З' : _ _ . .
Реклама
Аватара пользователя
dr.doc
Это не хвост, это антенна
Сообщения: 1368
Зарегистрирован: Вс мар 28, 2010 12:52:22
Откуда: Беларусь

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение dr.doc »

Да хватит стебаться. Написать вывод на семисегментник, по кнопке в порт, по UART в комп не проблема. Проблема, что на железо пока нет времени.
«Еще я хотел бы, чтобы наши ученые изобрели какой-то новый источник энергии, чтобы мы на коленях не ползали даже перед нашими братьями, умоляя их и выпрашивая тонну нефти или кубометр газа», — рассказал белорусский президент.
Реклама
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение B@R5uk »

Подскажите, пожалуйста, как правильно использовать ветвление в макросах?
Аватара пользователя
Kavka
Мудрый кот
Сообщения: 1810
Зарегистрирован: Чт июн 10, 2010 08:55:35
Откуда: Сибирские Афины

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение Kavka »

Как обычно - делаешь метку в тексте макроса и JMP/BRxx на неё из кода макроса.
Когда уже ничего не помогает - прочтите, наконец, инструкцию.
Лучший оптимизатор находится у вас между ушей. (Майкл Абраш, программист Quake и QuakeII)
Избыток информации ведёт к оскудению души - Леонтьев А. (сказано в 1965 г.)
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение B@R5uk »

А при многократном использовании макроса проблем не возникнет? Как компилятор разрешает дублирование меток в этом случае?
Аватара пользователя
Kavka
Мудрый кот
Сообщения: 1810
Зарегистрирован: Чт июн 10, 2010 08:55:35
Откуда: Сибирские Афины

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение Kavka »

Нет, проблем не возникает. Макрос обрабатывается особым образом. Метки объявленные в макросе видны только в коде макроса. Соответственно, можно делать одинаковые метки в разных макросах, и метку в макросе одинаковую с меткой в основной программе и это не будет вызывать ошибок и будет работать как надо. Для AvrAssembler2, который в 4ой, 5ой и 6ой Студии, вроде как, тут уже это обсуждалось и на практике подтверждали.
Когда уже ничего не помогает - прочтите, наконец, инструкцию.
Лучший оптимизатор находится у вас между ушей. (Майкл Абраш, программист Quake и QuakeII)
Избыток информации ведёт к оскудению души - Леонтьев А. (сказано в 1965 г.)
nirq
Опытный кот
Сообщения: 758
Зарегистрирован: Вс фев 10, 2013 15:26:00

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение nirq »

А скачать AVR Studio, установить, скомпилировать и посмотреть на файл *.lst?
Микросхема не знает никаких меток, у неё только инструкции, машинные коды. А в машинных кодах переходы выполняются как "перейти на адрес 1234 на плюс Х адресов или на минус Х адресов от самой команды перехода" - т.е. при компиляции выражение BRNE ERROR будет понято как BRNE ERROR-PC, где ERROR-PC = автоматически рассчитанная компилятором и подставленная в машинный код константа.
Или смысл в общении.
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение B@R5uk »

Kavka, спасибо за подробный ответ. Вы развеяли все мои опасения.
nirq писал(а):Или смысл в общении.
Да. Изображение
Аватара пользователя
ANALOG
Мучитель микросхем
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение ANALOG »

Сегодня столкнулся с таким багом при использовании avra на Linux:
Компилятор не проверяет значение параметров, передаваемых командам in/out lds/sts
Так, например, вот этот код (tiny2313)

Код: Выделить всё

sts TIMSK, R16
не вызвал у компилятора никаких подозрений, хотя программа, естественно, не работала. (TIMSK = 0x39, что меньше, чем 0x3F, так что тут нужен out) ушел почти день на отловку этого бага. :evil:
Пользуюсь avra уже давно, но на эту штуку наткнулся в первый раз, т.к. обычно использую макрос, который сам подстаяляет нужный out или sts, а тут понадобилось сделать вручную.
Ну зато пока ловил этот баг, повычищал прогу от всех остальных :)
Такие дела. :tea:
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение Alexeyslav »

а с чего это он должен проверять? константа имеет допустимое для STS значение... просто данные тулишь в какую-то ячейку памяти.
Аватара пользователя
ANALOG
Мучитель микросхем
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение ANALOG »

А, да, это я туплю :oops:
а уже компилятор заподозрил :roll:

Надо было все-таки все макросами писать :)
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение ARV »

ANALOG писал(а): TIMSK = 0x39, что меньше, чем 0x3F, так что тут нужен out
можно и STS, но с другим адресом, естественно
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Аватара пользователя
НАПАЛМ
Это не хвост, это антенна
Сообщения: 1314
Зарегистрирован: Пт ноя 27, 2009 19:47:13
Откуда: Казань

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение НАПАЛМ »

Здравствуйте.
Изучаю АЦП, камень - ATmega88PA. К 0-евому каналу подключаю переменник.
Сам код:
Спойлер

Код: Выделить всё

.include "m88PAdef.inc" ; Используем ATmega88PA
.include "macrobaselib.inc" ; Макроопределения

 
;======== RAM ============
      .DSEG         ; Сегмент ОЗУ
      
StrPtr:      .byte   2
UCSR0B_temp: .byte   1
        .equ MAXBUFF_IN 	=	10	; Размер в байтах
		.equ MAXBUFF_OUT 	= 	10
 
IN_buff:	.byte	MAXBUFF_IN	; Буфер приема
IN_PTR_S:	.byte	1		; Указатель начала
IN_PTR_E:	.byte	1		; Указатель конца
IN_FULL:	.byte	1		; Флаг переполнения
 
OUT_buff:	.byte	MAXBUFF_OUT	; Буфер передачи
OUT_PTR_S:	.byte	1		; Указатель начала
OUT_PTR_E:	.byte	1		; Указатель конца
OUT_FULL:	.byte	1		; Флаг переполнения. 

ADC_state:  .byte 1 ;Состояние АЦП (вкл/выкл)
RX_sel:		.byte 1	;Переменная состояния отправки данных
ADCH_sel:	.byte 1	;Переменная текущего канала АЦП
ADCCH:		.byte 8	;Восемь байт под хранение результатов АЦП. По байту на канал.
 
;======= FLASH ===========
      .CSEG         ; Кодовый сегмент 

.ORG 0x0000        ; (RESET) 
         RJMP   Reset
.include "ivectors.inc" // Подключим файл с таблицой прерываний
;----------------------------------------------------------------------
; Это обработчик прерывания. Тут, на просторе, можно наворотить сколько
; угодно кода. 

RX_OK:	
	
        PUSHF				; Макрос, пихающий в стек SREG и R16
		PUSH	R17
		PUSH	R18
		PUSH	XL
		PUSH	XH
 
		LDI	XL,low(IN_buff)		; Берем адрес начала буффера
		LDI	XH,high(IN_buff)
		LDS	R16,IN_PTR_E		; Берем смещение точки записи
		LDS	R18,IN_PTR_S		; Берем смещение точки чтения
 
		ADD	XL,R16			; Сложением адреса со смещением
		CLR	R17		    	; получаем адрес точки записи
		ADC	XH,R17
 
		LDS	R17,UDR0		; Забираем данные
		ST	X,R17			; сохраняем их в кольцо
 
		INC	R16			; Увеличиваем смещение
 
		CPI	R16,MAXBUFF_IN		; Если достигли конца 
		BRNE	NoEnd
		CLR	R16			; переставляем на начало
 
NoEnd:	
     	CP	R16,R18			; Дошли до непрочитанных данных?
		BRNE	RX_OUT		; Если нет, то просто выходим
 
 
RX_FULL:
    	LDI	R18,1			; Если да, то буффер переполнен.
		STS	IN_FULL,R18		; Записываем флаг наполненности
 
RX_OUT:		
        STS	IN_PTR_E,R16	; Сохраняем смещение. Выходим
		  

		POP	XH
		POP	XL
		POP	R18
		POP	R17
		POPF				; Достаем SREG и R16

		RETI

;***********************************
TX_OK:
        RETI
;***********************************

UD_OK:		
        PUSHF		   ; Пихаем в стек SPEG, R16, R17, R18, R19 
		PUSH	R17    ; и пару XL:XH	
		PUSH	R18
		PUSH	R19
		PUSH	XL
		PUSH	XH
 
 
		LDI	XL,low(OUT_buff)	; Берем адрес начала буффера
		LDI	XH,high(OUT_buff)
		LDS	R16,OUT_PTR_E		; Берем смещение точки записи
		LDS	R18,OUT_PTR_S		; Берем смещение точки чтения			
		LDS	R19,OUT_FULL		; Берем флаг переполнения
 
		CPI	R19,1			; Если буффер переполнен, то указатель начала
		BREQ	NeedSend	; Равен указателю конца. Это надо учесть.
 
		CP	R18,R16			; Указатель чтения достиг указателя записи?
		BRNE	NeedSend	; Нет! Буффер не пуст. Надо слать дальше
 
		LDI 	R16,(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0)|(0<<UDRIE0) 
		STS 	UCSR0B, R16		; Запрет прерывания По пустому UDR
		RJMP	TX_OUT			; Выходим
 
NeedSend:	
        CLR	R17			    ; Получаем ноль
		STS	OUT_FULL,R17	; Сбрасываем флаг переполнения
 
		ADD	XL,R18			; Сложением адреса со смещением
		ADC	XH,R17			; получаем адрес точки чтения
 
		LD	R17,X			; Берем байт из буффера
		STS	UDR0,R17		; Отправляем его в USART
 
		INC	R18			; Увеличиваем смещение указателя чтения
 
		CPI	R18,MAXBUFF_OUT		; Достигли конца кольца?
		BRNE	TX_OUT			; Нет? 
 
		CLR	R18			; Да? Сбрасываем, переставляя на 0
 
TX_OUT:		
        STS	OUT_PTR_S,R18		; Сохраняем указатель
 
		POP	XH
		POP	XL
		POP	R19
		POP	R18
		POP	R17
		POPF				; Выходим, достав все из стека

		RETI
;**************************************
ADC_OK:
        PUSHF
		PUSH	ZL
		PUSH	ZH
		PUSH	R17
 
		LDS	 R16,ADMUX ;Берем ADMUX
		ANDI R16,0b0001111 ;Маской отрезаем лишние биты. Получаем номер канала
					       ;С которого было снято измерение.
 
		;MOV	R17,R16		; Cохранили копию номера канала			
 
		LDI	ZL,low(ADCCH)	; Берем адрес начала массива с будущими данными. 
		LDI	ZH,High(ADCCH)
 
		ADD	ZL,R16		; Прибавляем к адресу наш номер канала.
					; Если было переполнение, то будет флаг С
		CLR	R16		; Флаг важен, а значение в R16 уже нет. Но нам нужен ноль
					; Возьмем и сделаем его из R16. 
		ADC	ZH,R16	; Сложим флаг возможного переполнения с ZH
					; Т.о. у нас получается в Z = адрес (ADCCH+номер канала)
					; И значения из разных каналов ложатся в разные переменные массива
					; с адресом базы ADCCH
 
		LDS	R16,ADCL	; Младшее значение нам не надо. Но считать его нужно. 
		LDS	R16,ADCH	; Берем в R16 значение из АЦП				
		ST	Z,R16		; Сохраняем его в массив по нужному адресу
					
 
        /*
		LDS	R16,ADMUX	; Опять взяли ADMUX
		ANDI	R16,0xF8	; На этот раз обнулили номер канала. Оставив остальные биты нетронутыми
 
		INC	R17		; Увеличили на 1 заныченный ранее номер канала
		ANDI	R17,0x07	; Обрезали лишние биты, чтобы не было переполнения. 
					; Число по маске 0х07 в принципе не может быть больше 7. 
					; А каналов у нас от 0 до 7. То что надо. 
 
		OR	R16,R17		; Слепили старое значение из ADMUX c новым значением номера 
		OUT	ADMUX,R16	; Канала. Т.е. по факту сделали MUX = MUX+1 выбрав следующий канал
					; Спихнули его в регистр ADMUX. Все, следующий канал выбран. Можно запускать
					; Следующее преобразование. */
 
		OUTI   ADCSRA,(1<<ADEN)|(1<<ADIE)|(1<<ADSC)|(0<<ADATE)|(3<<ADPS0)	; Запустили
 
		POP	R17		; Корректный выход из прерывания. 
		POP	ZH
		POP	ZL
		POPF
		RETI


;----------------------------------------------------------------------
 // Теперь инициализируем всё, что нам нужно.
 
Reset: 
 STACKINIT // Инициализация стека
 RAMFLUSH  // Очистка ОЗУ
 GPRFLUSH  // Очистка РОН (Регистров Общего Назначения)


.equ    XTAL = 16000000    
.equ    baudrate = 9600  
.equ    bauddivider = XTAL/(16*baudrate)-1
 
 
 //Инициализация USART
   LDI    R16, low(bauddivider)    ;Записываем в UBRR0H:UBRR0L 
   STS    UBRR0L,R16               ;делитель для настройки скорости передачи
   LDI    R16, high(bauddivider) 
   STS    UBRR0H,R16
 
   LDI    R16, 0
   STS    UCSR0A, R16
 
; Прерывания прием-передачи разрешены, прием-передача разрешена
   LDI    R16, (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0)|(0<<UDRIE0)
   STS    UCSR0B, R16   
 
; Формат кадра - 8 бит
   LDI    R16, (1<<UCSZ00)|(1<<UCSZ01)
   STS    UCSR0C, R16
    

 //Инициализация АЦП	
   OUTI   ADMUX,(1<<REFS0)|(1<<ADLAR)|(0<<MUX0)
   ;Опорное напряжение с AVCC, выравнивание влево, канал 0.


 //Мигание светодиодом для зрительного контроля рестарта микроконтроллера
   OUTI    DDRB,  0b11111101 //Инициализация порта B, PB0 - на выход
   RCALL LED_flashing
   
   SEI // Глобально разрешаем прерывания


Main:
   
   rcall Delay
   rcall Delay
   RCALL Buff_Pop ; Вызываем подпрограмму чтения из буфера приема USART
                  ; В R19 - код ошибки (1 - пустой буфер), R17 - данные.
   CPI R19, 1     ; Проверяем, пустой ли буфер.
   BREQ M1        ; Если буфер пуст, то идем на М1.

   RCALL Processing_of_external_commands
                  ; Процедура обработки команд, принимаемых по USART
		

               
M1:

   LDS R16, ADC_state
   SBRS R16, 0 ; Если АЦП запущен, то с него нужно отправить данные по USART
   rjmp M2 ; А если не запущен, то идем дальше

   LDS R19, ADCCH  ; Берем данные с 0-ого канала АЦП
   RCALL Buff_Push ; Кладем их в буфер 
   LDI 	R16,(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0)|(1<<UDRIE0) 
   STS 	UCSR0B, R16		; Разрешаем прерывания По пустому UDR
M2:

   rjmp Main  






        /*-------------------------------------
        | Начало сектора подпрограмм и функций |
         -------------------------------------*/  


;--------------Подпрограмма обработки команд внешнего управления-------*

Processing_of_external_commands:

   cpi R17, '0' ;Сравниваем R17 с нулем
   BREQ I1  ;Если пришел нуль, то гасим светодиод

   cpi R17, '1' ;Сравниваем R17 с единицей
   BREQ I2  ;Если пришла единица - зажигаем

   cpi R17, 'G' ;Сравниваем R17 c 'G'
   BREQ I3    ;Запускаем АЦП - Go

   cpi R17, 'S' ;Сравниваем R17 c 'S'
   BREQ I4    ;Выключаем АЦП - Stop

   rjmp Processing_of_external_commands_EXIT

I1:
   cbi  portb,0  ;Гасим светодиод
   rjmp Processing_of_external_commands_EXIT   
I2:
   sbi  portb,0  ;Зажигаем светодиод
   rjmp Processing_of_external_commands_EXIT
I3:
   cli
   LDI R16, 0b00000001
   STS ADC_state, R16
   sei
   OUTI   ADCSRA,(1<<ADEN)|(1<<ADIE)|(1<<ADSC)|(0<<ADATE)|(3<<ADPS0)
   
   rjmp Processing_of_external_commands_EXIT
I4:
   cli
   LDI R16, 0b00000000
   STS ADC_state, R16
   sei
   OUTI   ADCSRA,(0<<ADEN)|(0<<ADIE)|(0<<ADSC)|(0<<ADATE)|(3<<ADPS0)
    

Processing_of_external_commands_EXIT:   
 RET
;-----------------Конец подпрограммы-----------------------------------

               
;--------------Подпрограмма задержки [Delay]---------------------------*
Delay:
   LDI R20,0    

Loop:
   dec R20
   BRNE Loop

   dec R21
   BRNE Loop 
     
   ret
;-----------------Конец подпрограммы-----------------------------------


;--------Подпрограмма чтения из буфера USART---------------------------*
; OUT: 	R17 - Data,
;		R19 - ERROR CODE
Buff_Pop:
        LDS R16, UCSR0B      ; Берем в R16 значение UCSR0B
		STS UCSR0B_temp, R16 ; Сажаем предыдущее значение UCSR0B в ОЗУ  
        LDI R16, (0<<RXCIE0)|(0<<TXCIE0)|(0<<UDRIE0)
        STS UCSR0B, R16      ; Запрещаем прерывания от UART
                             ; для соблюдения атомарности.

		LDI	XL,low(IN_buff)	 ; Берем адрес начала буффера
		LDI	XH,high(IN_buff)
		LDS	R16,IN_PTR_E	 ; Берем смещение точки записи
		LDS	R18,IN_PTR_S	 ; Берем смещение точки чтения			
		LDS	R19,IN_FULL		 ; Берм флаг переполнения
 
		CPI	R19,1			 ; Если буффер переполнен, то указатель начала
		BREQ	NeedPop		 ; Равен указателю конца. Это надо учесть.
 
		CP	R18,R16			 ; Указатель чтения достиг указателя записи?
		BRNE	NeedPop		 ; Нет! Буффер не пуст. Работаем дальше
 
		LDI	R19,1			 ; Код ошибки - пустой буффер!
 
		RJMP	_TX_OUT		 ; Выходим
 
NeedPop:
    	CLR	R17			    ; Получаем ноль
		STS	IN_FULL,R17		; Сбрасываем флаг переполнения
 
		ADD	XL,R18			; Сложением адреса со смещением
		ADC	XH,R17			; получаем адрес точки чтения
 
		LD	R17,X		; Берем байт из буффера
		CLR	R19			; Сброс кода ошибки
 
		INC	R18			; Увеличиваем смещение указателя чтения
 
		CPI	R18,MAXBUFF_OUT		; Достигли конца кольца?
		BRNE	_TX_OUT			; Нет? 
 
		CLR	R18			; Да? Сбрасываем, переставляя на 0
 
_TX_OUT:	
        STS	IN_PTR_S,R18		; Сохраняем указатель

		LDS R16, UCSR0B_temp	;Загружаем в R16 предыдущее состояние UCSR0B
        STS UCSR0B, R16         ;Возвращаем обратно прерывания по USART

		RET
;-----------Конец подпрограммы-----------------------------------------


;--------Подпрограмма записи в буфер USART-----------------------------*
; IN R19 	- DATA
; OUT R19 	- ERROR CODE
Buff_Push:
        PUSH ZL
		PUSH ZH
        LDS R16, UCSR0B      ; Берем в R16 значение UCSR0B
		STS UCSR0B_temp, R16 ; Сажаем предыдущее значение UCSR0B в ОЗУ  
        LDI R16, (0<<RXCIE0)|(0<<TXCIE0)|(0<<UDRIE0)
        STS UCSR0B, R16      ; Запрещаем прерывания от UART
                             ; для соблюдения атомарности.
							  
		LDI	XL,low(OUT_buff)	; Берем адрес начала буффера
		LDI	XH,high(OUT_buff)
		LDS	R16,OUT_PTR_E		; Берем смещение точки записи
		LDS	R18,OUT_PTR_S		; Берем смещение точки чтения
 
		ADD	XL,R16		; Сложением адреса со смещением
		CLR	R17			; получаем адрес точки записи
		ADC	XH,R17
 
 
		ST	X,R19		; сохраняем их в кольцо
		CLR	R19			; Очищаем R19, теперь там код ошибки
						; Который вернет подпрограмма
 
		INC	R16			; Увеличиваем смещение
 
		CPI	R16,MAXBUFF_OUT		; Если достигли конца 
		BRNE	_NoEnd
		CLR	R16			; переставляем на начало
 
_NoEnd:	
    	CP	R16,R18			; Дошли до непрочитанных данных?
		BRNE	_RX_OUT		; Если нет, то просто выходим
 
 
_RX_FULL:
    	LDI	R19,1			; Если да, то буффер переполнен.
		STS	OUT_FULL,R19	; Записываем флаг наполненности
						    ; В R19 остается 1 - код ошибки переполнения
 
_RX_OUT:
    	STS	OUT_PTR_E,R16		; Сохраняем смещение. Выходим

		LDS R16, UCSR0B_temp	;Загружаем в R16 предыдущее состояние UCSR0B
        STS UCSR0B, R16         ;Возвращаем обратно прерывания по USART

		POP ZH
		POP ZL

        RET
;-----------Конец подпрограммы-----------------------------------------


;--------Подпрограмма стартового мигания светодиодом-------------------*
LED_flashing:
   
   OUTI    PORTB, 0b00000011 //Подтяжка на PB0 и PB1
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010 
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000011 
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010 
   rcall Delay
   rcall Delay
    OUTI    PORTB, 0b00000011 //Подтяжка на PB0 и PB1
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010 
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000011 
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010 
   rcall Delay
   rcall Delay
    OUTI    PORTB, 0b00000011 //Подтяжка на PB0 и PB1
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010 
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000011 
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010 
   rcall Delay
   rcall Delay
   RET
;-----------------Конец подпрограммы-----------------------------------


String:      .db   "23C  ",0 
;===== EEPROM ============
      .ESEG         ; Сегмент EEPROM (ППЗУ)
Сначала программа не хотела работать как надо - не реагировала на байты с компьютера, начал дебажить, выяснилось, что процессор даже не заходит в обработчик прерывания по приему, т.к. светодиод не загорался (или загорался и очень быстро гас - тут я не уверен); код зажжения светодида в обработчике в данный момент убрал.
По итогу, добавил после метки Main две строчки

Код: Выделить всё

Main:
   
   rcall Delay
   rcall Delay
и всё заработало - байты принимаются и обрабатываются. АЦП работает и шлет показания.
И такой вопрос: почему без делея не работает?
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение Мikа »

Привет, коты! На сей раз вопрос про АЦП. Гугл мне не очень помог, потому что все блоггерные умники, почему-тj напрочь влюблены в Си и пример на Ассемблере из быстро вылезающих сайтов мне ничего не попалось.
Вопрос у меня такой: как правильно пересчитать полученное значение их АЦП в напряжение?

Если я всё правильно понял, то формула такая:
(1.1* ADCH*4.03)/1024(или 1023?), где

1.1 - опорное U, берущееся внутри ATmega48
ADCH - то, что получилось с АЦП
4.03 - коэффициент делителя напряжения (10K и 3.3K)
1024 - выборки АЦП при 10битном АЦП (или нужно 1023?) Результат замера восьмибитный. 2 младших бита я не забираю.
Я думаю сделать так:

Код: Выделить всё

;Сначала 
.equ ADCco=(1.1*4.03)/1024 ;Просчёт с константами

;Потом уже после завершения преобразования:
Mov R16,ADCH             ;Загрузка результата АЦП в регистр
Ldi R17,ADCco             ;Загрузка результата вычисления констант
FMUL R16,R17             ;Беззнаковое умножение дробных чисел
Меня смущают дробные числа. Они очень весело описываются любителями си в бесполезных туториалах, мол "у нас точность измерения 0.0024 В!". Но, насколько я помню, дробные константы просчитает компилятор. А при вычислениях в МК там, вроде бы, всё округлится? А как тогда получить напряжение, скажем 2.7 В? Или представлять это как 2700 мВ?

Все это делается с целью следить за разрядом батареи, от которой питается все устройство, чтобы не дать ей переразрядиться.
Все это спрашиваю тут, потому что имею опыт сидеть целый день и не зная, что на самом деле происходит в регистрах? ломать голову. Не хочу потратить кучу времени зря. Помогите его спасти :) В принципе, куска программы с мало-мальскими комментариями, которая делает то, о чём я написал выше, будет достаточно. Я всё-таки хоть еще не радиокот, но я уже не радиокотёнок :D
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
Аватара пользователя
НАПАЛМ
Это не хвост, это антенна
Сообщения: 1314
Зарегистрирован: Пт ноя 27, 2009 19:47:13
Откуда: Казань

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение НАПАЛМ »

Если собираетесь юзать все 10 битов АЦП, то вам придется заморочиться с грамотной разводкой платы и работой АЦП со спящим процессором.
Курите даташит: стр.248 24.6 ADC Noise Canceler
Про разводку, обратите внимание на землю: http://hamradio.tomsk.ru/2013/04/14/пом ... стройства/
Аватара пользователя
ANALOG
Мучитель микросхем
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сообщение ANALOG »

Mika, в ADCco будет ноль, т.к. деление на 1024 даст меньше 1 и в итоге все занулится
А так да, лучше считать в милливольтах.
Конкретно для вашей задачи, если нужно просто контролировать, не опустилось ли напряжение ниже определенного предела, то я бы сказал, лучше вообще не трогать значения с АЦП (не переводить их в вольты), а просто сравнивать с заранее обсчитанной константой ;)
Ответить

Вернуться в «AVR»