Перейти к содержанию

Динамическая индикация на ассемблере


Юстас

Рекомендуемые сообщения

1 час назад, Геннадий сказал:

И перестаньте использовать пример учебника R16=TEMP

Именовать надо обязательно, иначе через месяц забудете зачем так сделали, либо придётся комментировать каждую строчку. У вас же в Си все переменные именованы и вас это не смущает

3 часа назад, o_l_e_g сказал:

Логичнее пользоваться командами

LSR Rn — логический вправо, при этом слева в байт лезут нули.
 LSL Rn — логический влево, при этом справа в байт лезут нули.

и через перенос.

ROL и ROR

вот так делать не надо, тем более в вашем случае. Вариант с массивом это хороший вариант, всегда можете поменять пины при изменении схемы, даже можете сделать таблицу и с портами, чтобы в качестве пина выбора индикатора использовать не только номер разряда, но и порт (PORTA, PORTB...)

 

23 часа назад, cucumber сказал:

cpi DigNum, 4 ; проверка на переполнение переменной номера разряда breq InitDigNum ; если переполнена - то обнуляем

во это тоже немного не понятно зачем сделано

я бы сделал так:
выделяете один регистр (у вас это DigNum) для счётчика вхождений в прерывание и в начале прерывания увеличиваете значение на 1 (inc DigNum), не заботясь о переполнении, а для выбора разряда из массива (смещения в массиве) используете andi DigNum,3 (т.е. выделяете только 2 младших бита - 4 комбинации, 4 индикатора) и результат этой операции прибавляете к адресу массива разрядов инжикатора

Порядок включения индикаторов должен быть следующий:

1) входите в прерывание - гасите все индикаторы (PORTD)
2) выставляете на шине данных подготовленный в main символ для отображения (PORTB)
3) включаете нужный разряд индикатора (PORTD), выходите из прерывания

1 час назад, cucumber сказал:

что в моем случае можно запихнуть в Main

Вы хотели отображать значение на семисегментном индикаторе, т.е. вы где то должны хранить значение от 0 до 9999 которое будет в будущем меняться. Можете для хранения выбрать как RAM так и регистровую пару (X,Y,Z)
Как уже говорили, выше для отображения символов на индикаторе вам необходим "буфер экрана" на 4 символа. При чём в данном буфере должны храниться непосредственно разряды для вывода на индикатор (чтобы не загружать код прерывания, а также чтобы снять ограничение на вывод определённых символов - например только цифр). Что нужно поместить в main? В main нужно поместить код преобразования двухбайтного числа, которое хранит, например, обороты (0..9999) в четыре байта "буфера экрана", ну а дальше по алгоритму...

Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

7 минут назад, dm37 сказал:

2) выставляете на шине данных подготовленный в main символ для отображения (PORTB)

на данный момент подготовленный символ уже подготовлен при инициализации, верно? И в прерывании по переполнению таймера 0 программа отображает этот символ, соответственно пока на данном этапе программа просто отображает и ничего больше не делает и main можно ничего не перемещать, так?

 

Ссылка на комментарий
Поделиться на другие сайты

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

да так

только сам вывод на индикатор сейчас надо поправить (вылизать один раз, дальше просто будите его использовать)

Изменено пользователем dm37
Ссылка на комментарий
Поделиться на другие сайты

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

там много переделывать и не нужно, всё необходимое в прерывании у вас уже есть, можно это уже назвать процессом оптимизации.

В 08.01.2017 в 00:24, cucumber сказал:

cli

вот это в прерывании не нужно, другие прерывания запрещаются автоматически при входе в прерывание и разрешаются при выходе

и ещё одно существенное замечание (пока main пустой это не актуально, а потом работать перестанет):

при входе в прерывании сохраняйте SREG, а также все регистры, которые вы меняете в прерывании, а при выходе восстанавливайте

Ссылка на комментарий
Поделиться на другие сайты

Менять там нужно ВСЕ. Перестаньте мыслить на Си! В данном случае, работа МК зависит от ВАШЕГО решения алгоритма, а не от библиотек стороннего разработчика. Представьте, что Вы намереваетесь использовать прерывание с более высоким приоритетом, чем таймер и напихаете в него (так же как и в данном решении) целую "бочку арестантов". Тогда ваша процедура индикации будет прываться на выполнение другого кода, возможно более длительного, чем индикация. Что получите в итоге?

Переделывайте, пока не увязли в программе. Рекомендую сделать так...

Заведите ячейку в RAM (ее лучше поименовать, в отличие от региcтра). Например, tik или takt (.equ takt=0x0060), В прерывании по переполнению таймера устанавливайте ее значение, отличное от 0 (например 1) и выходите из обработчика (больше в нем делать нечего).

В основном цикле проверяйте значение этой ячейки и если ее значение отличается от 0 выполняйте блок подпрограмм, не забывая в конце блока сбрасывать значение этой ячейки в 0. У Вас получится тактирование всего алгоритма идикации (по принципу одновибратора), которое легко отслеживать и менять длительность цикла по необходимости.
 

Ссылка на комментарий
Поделиться на другие сайты

3 часа назад, Геннадий сказал:

Представьте, что Вы намереваетесь использовать прерывание с более высоким приоритетом, чем таймер и напихаете в него (так же как и в данном решении) целую "бочку арестантов". Тогда ваша процедура индикации будет прываться на выполнение другого кода, возможно более длительного, чем индикация. Что получите в итоге?

Вы как раз и предлагаете, то что написали, когда вы закончите выполнять все многочисленные действия в main дойдёт дело и до обработки флага вывода на экран (возможно пройдёт время 2-3 флагов).
Может я что-то не понял, на как вы производите сложные вычисления, либо код, который по каким-либо причинам требующий "много" времени на выполнения (не надо говорить, что данного кода быть не может, сложная математическая обработка, фильтр..., который не прервёшь на середине выполнения)?

Меня не устраивает нестабильная обработка клавиатуры и индикации. Если есть код более важный, то понятно, что он работает в прерывании с более высоким приоритетом. И в нём будет кода ровно столько, сколько необходимо.

То, что вы предлагаете я использую как раз для некритичных участков (мой опыт так подсказывает)

Ссылка на комментарий
Поделиться на другие сайты

53 минуты назад, Геннадий сказал:

Dm37, Вы работаете на Си? 

да, Си и asm

я не вижу ни какой разницы между Си и asm при построении программы

Мало того, при использовании Си, я практически всегда просматриваю получившийся ассемблерный вариант кода, а в прерываниях ещё и обязательно смотрю время выполнения кода

Ссылка на комментарий
Поделиться на другие сайты

Считаете, что построение программы на ассемблере сильно отличается от Си (С++ не берём во внимание)?
В принципе, что вы предлагаете, можно легко реализовать на Си (вот только код мне не понравиться).
На Си я не могу применить, то что вытворял на ассемблере: выход из процедуры в произвольное место в зависимости от условий (типа оптимизации). Реализуется подменой адреса возврата.
Временные характеристики работы с периферией не рассматриваем (они часто и в Си реализуются ассемблерными вставками)

Ссылка на комментарий
Поделиться на другие сайты

В 09.01.2017 в 12:40, Геннадий сказал:

Рекомендую сделать так...

Геннадий, благодарю, я даже так и не догадался. Попробую так сделать и возьму на заметку. Пока же домучаю данный вариант. Сейчас получается вот так с учетом рекомендаций:

TIMER0_OVF:
         ;push tmp ; Сохр. tmp
         ;push tmp2 ; Сохр. tmp2
         ;in tmp2, SREG
            
         andi DigNum, 0b00000011  ;циклический "счетчик" прерываний
	 
	     ldi Temp, 0              ;"выключаем" дисплей
		 out PortD, Temp
 		 
		 ldi XH, high(Digits)     ;чтение из ОЗУ текущего значения разряда
         ldi XL, low(Digits)
         ldi Temp, 0
         add XL, DigNum
         adc XH, Temp
         ld  DigTemp, X

		 ; *********вывод в порт B   ABCDEFGH ************
		 ldi ZH,High(Array1*2)    ;загрузка начального адреса массива
         ldi ZL,Low(Array1*2)

         mov Temp, DigTemp        ;прибавление внутр. адреса
         add ZL, Temp
		 ldi Temp, 0
         adc ZH, Temp
		 lpm                      ;загрузка из ПЗУ

         mov Temp, R0             ;копирование в РОН
         out PortB, Temp          ;вывод в порт 
		
		 ; *********включение текущего разряда - вывод в порт D ************
		 ldi ZH,High(Array2*2)    ;загрузка начального адреса массива
         ldi ZL,Low(Array2*2)

         mov Temp, DigNum         ;прибавление внутр. адреса
         add ZL,Temp
		 ldi Temp, 0
         adc ZH, Temp
		 lpm                      ;загрузка значения из массива
		 mov Temp, R0             ;копирование в РОН
		 out PortD, Temp          ;вывод в порт 

		 inc DigNum               ;"двигаем" разряд	

		  
         ;out SREG, tmp2
         ;pop tmp2 ; Восстановление tmp2
         ;pop tmp ; Восстановление tmp
         reti                  ;выход из обработчика

 

Изменено пользователем cucumber
Ссылка на комментарий
Поделиться на другие сайты

 Допустим, я хочу построить тахометр. Верно ли я выстраиваю алгоритм: 

1. Добавляю прерывание по сигналу, с помощью этого прерывания считаю количество переполнений таймера n, также не забываю сохранить значения таймера на момент первого прерывания T1 и значение таймера на момент последнего прерывания по сигналу T2.

2. Имея эти 3 числа вычисляю S  - количество импульсов тактовой частоты между тактами исследуемого сигнала 

3. Вычисляю значение первого разряда, вычитая из S по тысяче, затем 2-го разряда - по сотне и тд.

Не учел пока проверку на переполнение (у меня же 4 разряда)  

??

Ссылка на комментарий
Поделиться на другие сайты

14 часа назад, cucumber сказал:

1. Добавляю прерывание по сигналу, с помощью этого прерывания считаю количество переполнений таймера n, также не забываю сохранить значения таймера на момент первого прерывания T1 и значение таймера на момент последнего прерывания по сигналу T2.

2. Имея эти 3 числа вычисляю S  - количество импульсов тактовой частоты между тактами исследуемого сигнала 

3. Вычисляю значение первого разряда, вычитая из S по тысяче, затем 2-го разряда - по сотне и тд.

Не учел пока проверку на переполнение (у меня же 4 разряда)  

чушь написал, не обдумал, сорри, кофе брэйк

 

Ссылка на комментарий
Поделиться на другие сайты

17 часов назад, cucumber сказал:

; *********вывод в порт B ABCDEFGH ************

у вас выводимые данные хранятся во Flash, а над в RAM (вы же их будите заполнять в main)

вы используете в прерывании регистр R0 и регистровую пару Z, их тоже надо сохранять при входе в прерывание

	push	temp
	in	temp,SREG
	push	temp
	push	DigNum
	push	R0
	push	ZL
	push	ZH
...
	pop	ZH
	pop	ZL
	pop	R0
	pop	DigNum
	pop	temp
	out	SREG,temp
	pop	temp
17 часов назад, cucumber сказал:

mov Temp, DigNum ;прибавление внутр. адреса
add ZL,Temp
ldi Temp, 0
adc ZH, Temp

может так

add ZL,DigNum
clr Temp
adc ZH, Temp
Ссылка на комментарий
Поделиться на другие сайты

3 часа назад, dm37 сказал:

у вас выводимые данные хранятся во Flash, а над в RAM (вы же их будите заполнять в main)

у меня в ПЗУ хранятся символы в массиве, а в ОЗУ значения разрядов, сначала я читаю 1 из 4-х байт ОЗУ, затем в зависимости от его значения читаю соответствующий символ в ПЗУ,  или мы о разном? У меня 10 цифр, я еще кстати хочу добавить 11-й символ для индикации переполнения.

3 часа назад, dm37 сказал:

вы используете в прерывании регистр R0...

пока написал заготовку

Ссылка на комментарий
Поделиться на другие сайты

2 минуты назад, cucumber сказал:

у меня в ПЗУ хранятся символы в массиве, а в ОЗУ значения разрядов, сначала я читаю 1 из 4-х байт ОЗУ, затем в зависимости от его значения читаю соответствующий символ в ПЗУ

надо не так, вы в main будете заполнять байты "буфера экрана" (4 байта - 4 индикатора). А в прерывании вы выдаёте байты из "буфера экрана" сразу на индикатор, без дополнительных преобразований. Т.е. "буфер экрана" не хранит ни какие числа, а хранит только сегменты индикатора. В эту схему легко укладывается 11-й символ, прерывание даже не будет знать о нём. Что вы засунули в "буфер экрана", то и будет отображаться.

и выкладывайте весь код, как ранее, а не только код прерывания (так нам понятнее будет, что вы делаете)

Ссылка на комментарий
Поделиться на другие сайты

6 минут назад, dm37 сказал:

. А в прерывании вы выдаёте байты из "буфера экрана" сразу на индикатор, без дополнительных преобразований

вроде я так и делаю, а в ОЗУ при инициализации записал произвольное число. Вот программа целиком:

  .nolist              
 .include "tn2313def.inc"	
 .list	
 .def temp = r16         ; рабочая переменная		        
 .def DigNum = r17       ; номер разряда
 .def DigTemp = r18      ; содержимое разряда текущее
 .dseg
 Digits:
 .byte 4                 ; отводим 4 байта ОЗУ для 4-х значений индикатора

 .cseg		                 
 .org 0x00	                

          ;ВЕКТОР ПРЕРЫВАНИЙ 
          rjmp  Reset               ;прерывание от RESET, ссылаемся на обработчик прерывания 
          reti; rjmp INT0           ; External Interrupt0 Handler
          reti; rjmp INT1           ; External Interrupt1 Handler
          reti; rjmp TIM1_CAPT      ; Timer1 Capture Handler
          reti; rjmp TIMER1_COMPA   ; Timer1 CompareA Handler
          reti; rjmp TIMER1_OVF     ; Timer1 Overflow Handler
		  rjmp TIMER0_OVF           ; Timer0 Overflow Handler
          reti; rjmp USART0_RXC     ; USART0 RX Complete Handler
          reti; rjmp USART0_DRE     ; USART0,UDR Empty Handler
          reti; rjmp USART0_TXC     ; USART0 TX Complete Handler
          reti; rjmp ANA_COMP       ; Analog Comparator Handler
          reti; rjmp PCINT          ; Pin Change Interrupt
          reti; rjmp TIMER1_COMPB   ; Timer1 Compare B Handler
          reti; rjmp TIMER0_COMPA   ; Timer0 Compare A Handler
          reti; rjmp TIMER0_COMPB   ; Timer0 Compare B Handler
          reti; rjmp USI_START      ; USI Start Handler
          reti; rjmp USI_OVERFLOW   ; USI Overflow Handler
          reti; rjmp EE_READY       ; EEPROM Ready Handler
          reti; rjmp WDT_OVERFLOW   ; Watchdog Overflow Handler
 
 .org 0x20	

 ;УСТАНОВКА СТЕКА
 Reset:   ldi temp,RAMEND        ;загрузка указателя стека
          out SPL,temp
  
 ;начальные настройки контроллера
	      ldi temp, 0b11111111    ;настройка портов
	      out DDRB, temp      
		  ldi temp, 0b01110100   
	      out DDRD, temp     

		  ldi temp, 0b00000010    ;разрешить прерывание по переполнению таймера0
		  out TIMSK, temp

		  ldi temp, 0b00000011    ;настройка предделителя тактовый сигнал  (011 /64, 100 /256, 101 /1024)
		  out TCCR0B, temp

		  ;ldi temp, 0x0C          ;инициализация компаратора
		  ;out OCR1AH, temp        ;загружаем в регистор сравнения число 19531
		  ;ldi temp, 0x0B     
		  ;out OCR1AL, temp

          ldi temp, 0             ;обнуление таймера 0
          out TCNT0, temp

		  ;ldi temp,0              ;обнуление таймера 1
          ;out TCNT1H, temp
          ;out TCNT1L, temp

		  sei ; Enable interrupts

 ;начальные значения переменных         
		  ldi DigNum, 0            ;начинаем индикацию с 0-го разряда

		  ldi temp, 0
		  sts Digits, temp         ;записываем произвольное для отображения число в ОЗУ     
		  ldi temp, 1
		  sts Digits+1, temp
		  ldi temp, 2
		  sts Digits+2, temp
		  ldi temp, 3
		  sts Digits+3, temp

Main:
  rjmp Main

;******************************
; обработчик прерывания таймера 0
;******************************
TIMER0_OVF:
         ;push tmp ; Сохр. tmp
         ;push tmp2 ; Сохр. tmp2
         ;in tmp2, SREG
            
         andi DigNum, 0b00000011  ;циклический "счетчик" прерываний
	 
	     ldi Temp, 0              ;"выключаем" дисплей
		 out PortD, Temp
 		 
		 ldi XH, high(Digits)     ;чтение из ОЗУ текущего значения разряда
         ldi XL, low(Digits)
         ldi Temp, 0
         add XL, DigNum
         adc XH, Temp
         ld  DigTemp, X

		 ; *********вывод в порт B   ABCDEFGH ************
		 ldi ZH,High(Array1*2)    ;загрузка начального адреса массива
         ldi ZL,Low(Array1*2)

         mov Temp, DigTemp        ;прибавление внутр. адреса
         add ZL, Temp
		 ldi Temp, 0
         adc ZH, Temp
		 lpm                      ;загрузка из ПЗУ

         mov Temp, R0             ;копирование в РОН
         out PortB, Temp          ;вывод в порт 
		
		 ; *********включение текущего разряда - вывод в порт D ************
		 ldi ZH,High(Array2*2)    ;загрузка начального адреса массива
         ldi ZL,Low(Array2*2)

         mov Temp, DigNum         ;прибавление внутр. адреса
         add ZL,Temp
		 ldi Temp, 0
         adc ZH, Temp
		 lpm                      ;загрузка значения из массива
		 mov Temp, R0             ;копирование в РОН
		 out PortD, Temp          ;вывод в порт 

		 inc DigNum               ;"двигаем" разряд	
		  
         ;out SREG, tmp2
         ;pop tmp2 ; Восстановление tmp2
         ;pop tmp ; Восстановление tmp
         reti                  ;выход из обработчика

Array1:
.db   0b00101000, 0b11111001, 0b00011100, 0b01011000, 0b11001001, 0b01001010, 0b00001010, 0b11111000, 0b00001000, 0b01001000 ; 9
Array2:
.db   0b00000100, 0b00010000, 0b00100000, 0b01000000

 

можно как-ниб сделать, чтобы текст не разъезжался?

Ссылка на комментарий
Поделиться на другие сайты

; *********вывод в порт B   ABCDEFGH ************
	ldi ZH,High(Array1*2)    ;загрузка начального адреса массива
	ldi ZL,Low(Array1*2)

	mov Temp, DigTemp        ;прибавление внутр. адреса
	add ZL, Temp
	ldi Temp, 0
	adc ZH, Temp
	lpm                      ;загрузка из ПЗУ

	mov Temp, R0             ;копирование в РОН

я про этот код - это чтение из Flash

должно быть как то так

	LDI	ZL,low(Digits)
	LDI	ZH,High(Digits)
 
 	add	ZL, DigNum
	clr	Temp
	adc	ZH, Temp
    
	LD	Temp, Z
	out	PortD, Temp

почитайте, здесь много полезного http://easyelectronics.ru/avr-uchebnyj-kurs-ispolzovanie-flash-rom.html

Ссылка на комментарий
Поделиться на другие сайты

символы (константы) должны храниться во flash, как это уже реализовано у вас, допишите в Array1 11-й символ и всё.

В main после разбора значения оборотов, вы сразу заполняете "буфер экрана" символами из flash (Array1). Если позволяет RAM, то можно, в принципе, из flash скопировать в RAM весь массив символов, но думаю в этом нет необходимости.

 

Ссылка на комментарий
Поделиться на другие сайты

LD     Temp,Z - работает с областью RAM. Для чтения из памяти программ (флеш) применяется другая команда - LPM. Вот только применять ее можно гибче...

Вместо: 

LPM

mov    temp,r0

читать сразу в temp:

LPM     temp,Z

Обрабатывать данные, преобразуя их в семисегментный код, можно когда угодно. Хоть при заполнении RAM, хоть при выводе очередного символа (на лету). Большой роли не играет.

Изменено пользователем Геннадий
Ссылка на комментарий
Поделиться на другие сайты

8 минут назад, Геннадий сказал:

Обрабатывать данные, преобразуя их в семисегментный код, можно когда угодно. Хоть при заполнении RAM, хоть при выводе очередного символа (на лету). Большой роли не играет.

Как угодно, только "буфер экрана", в этом случае, будет хранить не сегменты индикатора, а код символа (я считаю, что хранить сегменты индикатора более гибкое решение)

Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу

×
×
  • Создать...