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

Обсуждаем контроллеры компании Atmel.
Ответить
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1925
Зарегистрирован: Чт июл 28, 2016 07:58:37
Откуда: Kyiv, UA

Сообщение GoldenAndy »

a1000, вот честно, в такой закрученной "циклической" логике с кучей переходов, базируясь на flags, я до конца не отследил что и как работает.
Сделайте один цикл на 7 итераций и в нем просто читайте 7 байт данных, складывая их в память и инкрементируя указатель.
Чтение по i2c с точки зрения МК - достаточно медленное, но я не думаю, что у вас прям такой реалтаймовый проект, когда не сделать чтение 3231 на ожиданиях.
Плюсом такого цикла будет то, что установка всех режимов, регистров, флагов i2c будет в одном месте. Включая ACK для всех байтов, кроме последнего, и NACK для последнего.
И тогда вы не наступите на те грабли, которые я вижу на первый взгляд.

У вас во втором примере чтение секунд и минут выполняется макросом/подпрограммой IIC_RCV (RCALL IIC_RCV) - судя по всему, это чтение с флагом ACK.
А вот часы вы читаете макросом IIC_RCV2 - это чтение с NACK - последний байт.
После этого 3231 понимает, что от него уже ничего не ждут, и ничего не отдает. вот вам и 0xFF.
Кроме того, вы читаете день недели, число, месяц и год тем же макросом - IIC_RCV2 - без ACK.

Замените везде, кроме последнего байта (год) RCALL IIC_RCV2 на RCALL IIC_RCV
ИзображениеИзображение
Изображение
 
Telegram               Лучшая благодарность ->
[+]
Контактная информация:
Реклама
Первый раз сказал Мяу!
Аватара пользователя
Сообщения: 31
Зарегистрирован: Вс окт 18, 2020 19:01:52
Откуда: Харьковская область

Сообщение a1000 »

GoldenAndy, вы абсолютно правы. Совсем забыл про NACK, вот и скопировал не глядя. Спасибо за помощь, исправил, всё заработало.
Это Спарта! В смысле, ассемблер. Все ручками.
Реклама
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1408
Зарегистрирован: Чт авг 21, 2014 11:11:48
Откуда: краснодарский край

Сообщение главный колбасист »

А почему у меня команда ld не работает в студио-симуляторе для tiny2313 ?
Ошибка компиляции,неправильный регистр. Регистр длинный r30,адрес
ячейки туда загружен и такой и сякой.Все равно ошибка.
Контактная информация:
Вымогатель припоя
Сообщения: 615
Зарегистрирован: Вс дек 28, 2014 21:54:05

Сообщение dgrett »

А адрес озу у Вас где? Всю строку пж покажите.
Я всё-всё узнAю и стану профессором.
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Аватара пользователя
Сообщения: 15816
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

[uquote="главный колбасист",url="/forum/viewtopic.php?p=4154711#p4154711"]А почему у меня команда ld не работает в студио-симуляторе для tiny2313 ?
Ошибка компиляции,неправильный регистр. Регистр длинный r30,адрес
ячейки туда загружен и такой и сякой.Все равно ошибка.[/uquote]
Регистровая пара Zh:Zl она же R31:R30
Диапазон адресов для команд LD rd,Z , LDS rdZ+rel касается только ОЗУ (для тини2313 это 0х0000-0x00DF)
Для доступа к таблицам в ПЗУ используется группа команд LPM
8)
Реклама
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1408
Зарегистрирован: Чт авг 21, 2014 11:11:48
Откуда: краснодарский край

Сообщение главный колбасист »

ldi r30,0xdf
ld r20,r30
Ну вот...
И вот.
C:\AVRStudio\belov\belov.asm(50): error: Invalid register
Контактная информация:
Реклама
Первый раз сказал Мяу!
Аватара пользователя
Сообщения: 31
Зарегистрирован: Вс окт 18, 2020 19:01:52
Откуда: Харьковская область

Сообщение a1000 »

Боюсь ошибиться, но по моему, LD работает только с регистровой парой. Попробуйте так

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

ldi r30,0xdf
clr r31
ld r20,Z
Это Спарта! В смысле, ассемблер. Все ручками.
Друг Кота
Аватара пользователя
Сообщения: 15816
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

главный колбасист писал(а):
ldi r30,0xdf
ld r20,r30
...
Где Вы откопали
ld r20,r30
???
:music:
Вторым регистром в команде LD может быть указан X, Y или Z
или как слэнг первый регистр регистровой пары - в данном случае R31 (но уж никак не R30)
8)
шпора AVR.pdf
(60.25 КБ) 180 скачиваний
:beer:
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1925
Зарегистрирован: Чт июл 28, 2016 07:58:37
Откуда: Kyiv, UA

Сообщение GoldenAndy »

LD на вход требует не имя регистра, а имя регистровой пары
Причем это имя может быть только X, Y или Z, оно там в команде кодируется всего двумя битами.
И оно захардкоджено прямо в асме, как и регистры R0-R31

Изображение

R26-R31 переопределены как XL,XH,YL,YH,ZL,ZH в регистрово-битовых определениях контроллера
Изображение
Вложения
R26-31.jpg
(11.78 КБ) 563 скачивания
ldi.jpg
(18.01 КБ) 567 скачиваний
ИзображениеИзображение
Изображение
 
Telegram               Лучшая благодарность ->
[+]
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 15816
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

Однако все, что указано в *.inc файлах может быть переопределено пользователем в прикладной программе (дополнительный файл определений).
8)
и вот еще момент...
допустим мне форма

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

ld r16,z 
не сильно нравится...
тогда

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

#define pointer_2 z
#define tmp0 r16
и в самой программе

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

ld tmp0,pointer_2
Касательно регистров рон, ранее получивших определение (чтоб компилятор матюки не извергал).
Для тех же

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

; ***** CPU REGISTER DEFINITIONS *****************************************
.def	XH	= r27
.def	XL	= r26
.def	YH	= r29
.def	YL	= r28
.def	ZH	= r31
.def	ZL	= r30
переопределяем не регистр, а его ранее выполненное определение

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

#define ptrd_h XH
#define ptrd_l XL
и далее уже используем ptrd_h вместо XH (но никак не вместо Х !!!)
8)
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1925
Зарегистрирован: Чт июл 28, 2016 07:58:37
Откуда: Kyiv, UA

Сообщение GoldenAndy »

BOB51, не путайте вопрошающего :)
ИзображениеИзображение
Изображение
 
Telegram               Лучшая благодарность ->
[+]
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 15816
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

Его запутаеш!
8)
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1408
Зарегистрирован: Чт авг 21, 2014 11:11:48
Откуда: краснодарский край

Сообщение главный колбасист »

ld r20,Z
Так получается. ошибок нет. :)

Добавлено after 9 hours 11 minutes 29 seconds:
Где Вы откопали
ld r20,r30
???
:music:
Это я сама придумала. что с меня возьмешь. :oops:

Вообще хотелось по ходу цикла вычищать содержимое и без того небольшой озу ,в случае использования rcall вместо rjmp, чтобы
не переполнялся стек,и через полминуты работы симулятора не выскакивала ошибка переполнения.
Бред,конечно. не обессудьте... :)
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 15816
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

Переполнение стека обусловлено несоответствием количества переходов rcall количеству возвратов ret (или прерываний - по сути те же rcall только аппаратными средствами с возвратом по reti).
Симулятором причину прекрасно можно отследить. Как само значение в указателе стека, так и того, что в стек попадает.
8)
Вымогатель припоя
Сообщения: 615
Зарегистрирован: Вс дек 28, 2014 21:54:05

Сообщение dgrett »

Ничего не понял. Вы что-то не так делаете.
Я всё-всё узнAю и стану профессором.
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1408
Зарегистрирован: Чт авг 21, 2014 11:11:48
Откуда: краснодарский край

Сообщение главный колбасист »

Ничего не понял. Вы что-то не так делаете.
Конечно я не так все делаю. Я вообще не понимаю что я тут делаю.(в этой реальности) :o

Ааа... ret у меня вообще ни одной строчки нет.Значит надо вставить ret и этот геморрой закончится? :)
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 15816
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

Каждому rcall по своему ret.
И каждому прерыванию по личному reti.
Тогда все довольны будут.
8)
Первый раз сказал Мяу!
Аватара пользователя
Сообщения: 31
Зарегистрирован: Вс окт 18, 2020 19:01:52
Откуда: Харьковская область

Сообщение a1000 »

[uquote="главный колбасист",url="/forum/viewtopic.php?p=4155615#p4155615"]Ааа... ret у меня вообще ни одной строчки нет.Значит надо вставить ret и этот геморрой закончится? :)[/uquote]
Мне даже стало интересно, как-же вы из подпрограмм возвращаетесь?
Это Спарта! В смысле, ассемблер. Все ручками.
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1925
Зарегистрирован: Чт июл 28, 2016 07:58:37
Откуда: Kyiv, UA

Сообщение GoldenAndy »

главный колбасист, в вашей задаче чтения данных из 3231 достаточно было обойтись парой вложенных циклов и ветвлений. код получился бы достаточно компактный, но при этом гораздо более читаемый и без повторений одинаковых операций в разных местах...
А вот так запутанно бегать по коду - вы и сами запутаетесь, и код будет нечитаем. И ошибку с ACK/NACK легко можете пропустить.
Ну и экономия ОЗУ актуальна только если оно вам нужно для обработки большого массива каких то данных... Обычно программная память кончается быстрее :) Соответственно, код лучше оптимизировать. Например, вот так:

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

1.  Настройка i2c
2.  i = 0
3.  IIC_START i2c
4.  IIC_SEND (3231_ADDR+i)
5.  if (i = 0) 
    then begin
6.       IIC_SEND 0x00 ;адрес, с которого мы будем читать данные из 3231
7.       goto 16
         end
    else begin
8.       j = 7 ; число байт, которые надо прочитать
9.       Z = (адрес области памяти для чтения из 3231)
10.      j = j - 1
11.      if (j > 0) then IIC_READ_ASK
                    else IIC_READ_NASK
12.      save DATA -> Z
13.      Z = Z + 1
14.      if (j > 0) then goto 10
15.      end ; конец блока else для if (i=0)
16. i = i + 1
17. if ( i < 2 ) then goto 3
18. IIC_STOP   
Я вот тут вам как то накидал в псевдокоде алгоритм чтения данных из 3231 (это "перепевка" из какой то из моих старых программ... Написана на некоем подобии языка высокого уровня, что бы было легче разобраться)

Обратите внимание, в строке 4 адрес 3231 должен задаваться в 8-битном формате - 1101000х (0xD0) - тогда для записи, когда i=0 - как раз уйдет адрес с режимом записи 11010000, а когда i станет 1 - по шине уйдет адрес 11010001 - режим чтения. Т.е. не нужно писать 2 отдельных блока для отправки адреса для записи и адреса для чтения.

Точно так же старт и повторный старт выполнятся в одном месте.

По использованию ОЗУ - нужно только 7 байт для данных из 3231, а так же 2 байта стека для вызова подпрограмм общения с i2c.
По регистрам - ZL/ZH на ссылку в ОЗУ, 2 регистра для i,j - переменных цикла, да пара временных регистров для общения с i2c

IIC_START, IIC_SEND, IIC_READ_ASK, IIC_READ_NASK, IIC_STOP - это подпрограммы работы с модулем i2c для вашего МК. Должны вызываться посредством CALL/RCALL

Сам блок работы с i2c
Спойлер

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

;--------------------------------------------------------
; I2C Code
;--------------------------------------------------------
; по мотивам DiHalt'a
;--------------------------------------------------------
; Работа с IIC передатчиком. Предупреждаю -- это быдлокод!
; Несмотря на то, что так делают почти все :) 
; Но это медленно и тормозно, особенно в свете использования
; RTOS. Почему? Да потому что использует глухое
; ожидание флага готовности IIC. Вместо того, чтобы отдать 
; управление диспетчеру задач. Также нет обработоки ошибок
; и какой либо оценки прошла команда или нет. Просто пример!
;---------------------------------------------------------
; IIS_START — устраивает на шине Start состояние
; IIC_SEND — посылает байт, просто байт. Не важно что это — адрес, данные…
; IIC_READ_ASK — команда противополжная предыдущей — принимает байт в конце дает ACK
; IIC_READ_NASK — тоже принимает байт, но в конце дает NACK, используется для приема последнего байта, 
; 	             чтобы дать понять Slave устройству что мы в его услугах больше не нуждаемся.
; IIS_STOP — устраивает на шине Stop состояниеи освобождает линию
;---------------------------------------------------------
; Последовательность инициализации TWI (i2c)-интерфейса
; Рассматривается только Master-режим
; В регистр TWBR, а так же в биты 0,1 (TWPS0:1) регистра TWSR записываются константы для определения скорости шины.
; (констатны можно рассчитать на AVRCalc)
;---------------------------------------------------------
; Последовательность чтения для IIC, на примере обычной EEPROM типа АТ24Схх обычно такая:
;	Старт
;	Адрес дейвайса[write]   
;	Адрес ячейки откуда будем читать
;	Повторный Старт,
;	Адрес девайса [read],
;	Байт из адресованой ячейки,
;	Байт из адресованой ячейки+1,
;	Байт из адресованой ячейки+2
;	….
;	Cтоп
;
; Последовательность записи для IIC обычно такая:
;	Старт
;	Адрес девайса [write]
;	Адрес ячейки куда будем писать
;	Данные раз
;	Данные два
;	Данные три
;	…..
;	Стоп
;  [write] & [read] - младший бит адреса. =0 - запись, =1 - чтение
;
;---------------------------------------------------------
; примеры для работы с часами - см. http://easyelectronics.ru/chasy-realnogo-vremeni-pcf8583.html

;---------------------;


; Даем старт на линию I2C
; Портит R16,SREG
IIC_START:
			LDI 	R16,1<<TWINT|1<<TWSTA|1<<TWEN|0<<TWIE
			OUT	TWCR,R16
IIC_S:		
			IN	R16,TWCR
			ANDI	R16,1<<TWINT			
			BREQ	IIC_S			; Ждем пока передатчик IIC выполнит старт
			RET

;-----------------------------------------------------------------------------
;Посылаем байт (R16) по IIC 
; Портит R16,SREG
IIC_SEND:  	
			OUT	TWDR,R16
			LDI 	R16,1<<TWINT|1<<TWEN|0<<TWIE
			OUT	TWCR,R16
IIC_B:		
			IN	R16,TWCR
			ANDI	R16,1<<TWINT	; Ждем пока передатчик пошлет байт			
			BREQ	IIC_B
			RET

;-----------------------------------------------------------------------------
; Принять байт. 
; Принятый байт в R16
; Портит SREG
IIC_READ_ASK:
			LDI		R16,1<<TWINT|1<<TWEN|1<<TWEA|0<<TWIE
			OUT	TWCR,R16
IIC_R:		
			IN	R16,TWCR
			ANDI	R16,1<<TWINT			
			BREQ	IIC_R			; Ждем пока байт будет принят
			IN	R16,TWDR
			RET

;-----------------------------------------------------------------------------
; Принять последний байт. Помните я писал о разнице между просто байтом и последним
; байтом? В последнем байте не генерируется ACK!!!
; Принятый байт в R16
; Портит SREG

IIC_RREAD_NACK:
			LDI 	R16,1<<TWINT|1<<TWEN|0<<TWEA|0<<TWIE
			OUT	TWCR,R16
IIC_R2:		
			IN	R16,TWCR
			ANDI	R16,1<<TWINT	; Ждем пока байт будет принят		
			BREQ	IIC_R2
			IN	R16,TWDR
			RET

;-----------------------------------------------------------------------------
; Сгенерировать STOP
; Портит R16,SREG
IIC_STOP:	
			LDI R16,1<<TWINT|1<<TWSTO|1<<TWEN|0<<TWIE
			OUT	TWCR,R16
IIC_ST:		
			IN	R16,TWCR
			ANDI	R16,1<<TWSTO			
			BREQ	IIC_ST			; Ждем пока не будет готов стоп.
			RET
Последний раз редактировалось GoldenAndy Пт янв 07, 2022 01:15:59, всего редактировалось 1 раз.
ИзображениеИзображение
Изображение
 
Telegram               Лучшая благодарность ->
[+]
Контактная информация:
Первый раз сказал Мяу!
Аватара пользователя
Сообщения: 31
Зарегистрирован: Вс окт 18, 2020 19:01:52
Откуда: Харьковская область

Сообщение a1000 »

Я так понял, что предыдущий пост адресован мне. :))
Да, я видел такую реализацию у DI HALT. Но он сам-же писал о таком методе
Работа с IIC передатчиком. Предупреждаю -- это быдлокод!
Несмотря на то, что так делают почти все :)
Но это медленно и тормозно, особенно в свете использования
RTOS. Почему? Да потому что использует глухое
ожидание флага готовности IIC.
Я не спорю, метод простой, рабочий и широко распространённый. Но я занимаюсь программированием не для комерции, а ради высокого искусства. Вот захотелось сделать на прерываниях, а оно для I2C одно. Вот и сгородил такой лабиринт.
А на счёт памяти вы правы. Пишу для ATmega8 и HEX уже 6,6 KB. А хотелось ещё DS18B20 прикрутить. Наверное прийдётся на 328 мегу перейти.
Это Спарта! В смысле, ассемблер. Все ручками.
Ответить

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