Учусь писать программу на asm, возник вопрос - у меня есть циклы считывания 50 временных интервалов сигнала и время каждого интервала нужно записать в свою ячейку оперативной памяти для дальнейшего вывода на индикацию - вопрос: как численно и коротко адресоваться к массиву ячеек для сохранения в них результата счета? Я пока придумал такой вариант - приращение значения регистра по счетчику команд с каждым переполнением счетчика таймера. Пример: REG1 equ 20h ;объявление регистров REG2 и т.д. REG50 equ 52h Chet equ 53h ; счетчик интервалов ****************** тело программы ****************** ;приращение результатов movlw .1 ; первая ячейка subwf Chet,0 btfss STATUS,C incf REG1,1 ; если в счетчике значение 1 - то приращение идет в первой ячейке памяти movlw .2 subwf Chet,0 btfss STATUS,C incf REG2,1 ;если 2 - то во второй и т.д. movlw .50 subwf Chet,0 btfss STATUS,C incf REG50,1 ;если 50 - то в 50-й
только получается очень много кода - только на счет 200 строк
что за МК (10,12,16 или 18-я серии, энхансед среднемладшие)? что откуда читаем и куда пишем (таблица из ПЗУ в ОЗУ или чтение/запись массива данных в пределах ОЗУ)?
[code]REG1 equ 20h ;объявление регистров Chet equ 53h ;[/code] .. movlw .1 ; первая ячейка subwf Chet,0 btfss STATUS,C incf REG1,1 ; если в счетчике значение 1 - то приращение идет в первой ячейке памяти
Явно не ПЗУ
_________________ У того, кто делает — получается редко. У того, кто не делает — не получается никогда.
MK Pic16F648A - Считаем количество переполнений таймера TMR0 - в регистре оперативной памяти "К" - а далее нужно выгрузить это значение в регистр ОП с 1 по 50. А нельзя обратиться к оперативной памяти по номеру ячейки (плюс Сhet)?
Шозабред? Зачем для упорядоченного массива данных писать табличный код непосредственной адресации? BOB51 всё объяснил. Обращение к массивам данных реализуют через индексы или указатели (что в общем одно и тоже по сути). Для этих целей в обсуждаемой архитектуре есть специальный регистр-указатель (FSR) и виртуальное "окно" доступа (INDF) по этому указателю. ЗЫ. Даже если массив и неупорядочен, то таблица может пересчитывать адрес для FSR, но реализовывать кучу вариантов кода путем непосредственной адресации (адрес данных в инструкции) - это идиотизм.
и ссыль на учебно -практическую документацию на русском: http://www.microchip.ru/lit/?mid=1x0 там на выбор изучающего. PIC16F62X как раз про 627 - 628 (более ранние версии 648го)
Разобрался! Все на первый взгляд очень просто устроено - если записать в регистр FSR номер ячейки памяти - то в регистре INDF будет значение этой ячейки, с которым можно работать как с самой ячейкой. И код тогда нужен примерно такой:
Код:
REG1 equ 20h ;объявление регистров REG2 и т.д. REG50 equ 52h Chet equ 53h ; счетчик интервалов ****************** тело программы ****************** movlw .19 addwf Chet,0 ; теперь в аккумул номер нужной ячейки упорядоченного блока памяти (а можно вообще убежать в ОП четвертого банка памяти!) movwf FSR ; записали номер ячейки в FSR - теперь INDF - это виртуально она же movf TINTERVALA,0 ; переслали значение n-го интервала времени в аккум movwf INCF ; все - в ячейке 20h (и далее) сохранено значение времени
Продолжу тему. Заглянул в Proteus7,7 sp2 - SAMPLES\VSM for PIC16\PIC Doorbell\ Увидел там вот такие команды ... movlw tune0 / 0x100 ; как это понимать? movwf tptrh movlw tune0 % 0x100 ; как это понимать? movwf tptrl ... Полный текст программы. Спойлер
initialise ; Register set up: clrw ; Zero. movwf PORTA ; Ensure PORTB is zero before we enable it. movwf PORTB ; Ensure PORTB is zero before we enable it. bsf STATUS,RP0 ; Select Bank 1 clrf TRISB ; Port B is outputs
; Set up timer 0 for tone generation movlw 0x02 ; Prescaler on, internal clocking, divide by 4 movwf OPTION_REG bcf STATUS,RP0 ; Back to bank 0
; Poll for a button wait clrf PORTB wloop btfss PORTA,0 goto playtune0 btfss PORTA,1 goto playtune1 goto wloop
;Routines to play the tunes playtune0 movlw 0x13 movwf PORTB movlw 0x50 movwf tempo movlw tune0 / 0x100 movwf tptrh movlw tune0 % 0x100 movwf tptrl goto playtune
;Subroutine to play a tune playtune call gettunedata ; Lookup note from tune table movwf note ; Store the note code btfsc note,7 ; Bit 7 set => end of tune goto wait ; Back to waiting for a button press incf tptrl,F ; Increment the tune pointer call gettunedata ; Get the note length movwf length ; Store it
; Play the stored note ; The tune table supports two octaves and 8 note lengths. playnote btfsc note,6 ; Bit 6 set => rest goto playwait ; Silence ensues... movf note,W ; Retrieve the note andlw 0x3F ; Mask off the pitch index call pitchtable ; and look it up in the pitchtable movwf pitch ; Transfer the value to Timer 0 movwf TMR0 bcf INTCON,T0IF ; Clear Timer Interrupt bsf INTCON,T0IE ; Enable interrupts for Timer 0 bsf INTCON,GIE playwait movf length,W ; Retrieve the note length movwf dl1 ; and store it in delay counter 1 loop1 movf tempo,W ; This value sets the timing resolution movwf dl2 loop2 nop ; Inner delay loop delay nop decfsz dl2,F goto loop2 decfsz dl1,F ; Outer delay loop goto loop1 bcf INTCON,T0IE ; Timer interrupts off
;Fetch next note/length pair: incf tptrl,F ; Increment tune ptr btfsc STATUS,Z ; Test for low byte rollover incf tptrh,F ; Inc high byte goto playtune ; Loop for next note
; Routine to fetch data from the tune tables gettunedata movf tptrh,W ; Set up PCLATH for the jump movwf PCLATH movf tptrl,W ; Get low address of tptr movwf PCL ; Jump off to the table entry
;Interrupt service - toggle the drive polarity to the sounder ;Note that this does not preserve the status bits - be careful, ;and that there is an assumption we are addressing bank 0. toggle movwf temp movf pitch,W movwf TMR0 movf PORTB,W xorlw 0x0F movwf PORTB bcf INTCON,T0IF movf temp,w retfie
Так и понимать: movlw tune0 / 0x100 => WREG=tune0/0x100, где tune0 - константа, которая должна быть объявлена в коде или в хедере. Соответственно movlw tune0 % 0x100 => WREG=остаток от деления (tune0/0x100) Все это математика препроцессора. Но так как tune0 - это МЕТКА, то есть по факту АДРЕС, то значение адреса и будет константой.
В коде tune0 прибит гвоздями к конкретному адресу:
Код:
org 0x0080
; Tune 0 Data ; Bach 'Badinerie' from Suite No 2 tune0 retlw 0x17 ;B retlw 0x78 ;240 retlw 0x1A ;D
То есть tune0=0х0080 Но код написан так, что tune0/tune1 могут быть размещены на произвольный адрес. Поэтому, чтобы изменение расположения таблиц не требовало изменения констант в коде, применен расчет страницы (деление на 0х100) и смещения в странице (остаток от деления) препроцессором. Посмотрите содержимое флеша (дизасм), там нет никаких делений и подставлены уже результирующие константы. ЗЫ. Вообще метод расчета констант из меток - плохая идея. При перемещаемом коде (линкер определяет позицию метки) всё это перестает работать. Патамушта препроцессор работает ДО линкера и он не может получить значение метки.
Похоже там еще и "прыжки по указателю" задействованы... А это уже в комплекте с PCLATH пойдет (вычисляемый переход)... в зависимости от садизма автора исходника.
Последний раз редактировалось BOB51 Пн дек 28, 2020 17:01:11, всего редактировалось 1 раз.
Очень полезный, но требует весьма много внимания и аккуратности. Поскольку во многих случаях надо где-то хранить предшествующее значение PCLATH, а у среднемладших прямое помещение в аппаратный стек... проблематично - надо делать программный стек. Второе - нет переноса из PCL (при операциях, где результат операции размещается в PCL) при переполнении в PCH. В то же время прием "вычислительного перехода" по адресу, формируемому содержимым "PCLATH:результат операции с PCL", специфичный именно для среднемладших ПИКов, весьма удобная штука.
Может быть Вы правы. Вот такое обращение с PCLATH тоже решает все проблемы. Спойлер
Код:
;---------- TABLE movlw high PC_ADR ; Установка (через W), в PCH (посредством PCLATH), числового movwf PCLATH ; значения старшего байта адреса той команды, которая помечена ; меткой PC_ADR. ;---------- ; Будем “прыгать” в текущий PC-блок или в следующий? ;---------- movf Reg,W ; “Прыжковое” число копируется в W. addlw low PC_ADR ; Суммирование "прыжкового" числа и числового значения младшего ; байта адреса той команды, которая помечена меткой PC_ADR. btfsc STATUS,C ; Перенос был или нет? incf PCLATH,F ; Если перенос был (соответствует работе в следующем PC-блоке), ; то PCLATH+1=... (то есть, PCH+1=...). ; Если переноса не было (соответствует работе в текущем ; PC-блоке), то команда инкремента не исполняется. movf Reg,W ; “Прыжковое” число копируется в W. ;---------- ; Таблица вычисляемого перехода. ;---------- addwf PCL,F ; PCL + W = ... PC_ADR retlw b'00111111' ; ..FEDCBA -> символ “0” 3Fh retlw b'00000110' ; .....CB. -> символ “1” 06h
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения