АвторСообщение
администратор




Пост N: 613
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 12.02.13 14:21. Заголовок: Систематизация кода и оформление программ.


В процессе написания программы зачастую возникают сложности и вопросы по её ПРАВИЛЬНОМУ оформлению. Особенно важно это начинающему, потому как лучше сразу привыкать к правильному и удобному оформлению и написанию, чем потом "переучиваться".
Одной из моих настольных книг является пособие Виктора Тимофеева (Tester) "Как правильно оформлять программы на ассемблере для PIC-контроллеров".
Очень грамотно, наглядно и доходчиво изложен материал.
Но хочется продолжения!
Здесь можно задавать свои вопросы, либо делиться наработками, алгоритмами, ПП, примерами.


Спасибо: 0 
ПрофильЦитата Ответить
Ответов - 22 [только новые]


администратор




Пост N: 608
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 04.02.13 16:40. Заголовок: А ни кто и не спорит..


Несколько постов были перенесены из темы Куда пропал Корабельников E.A.

А ни кто и не спорит. Любая толковая информация, помогающая в освоении предмета изучения и не содержащая ошибок или противоречий, есть благо. Каждый ищет то, что ему легче в понимании и освоении. Потому их и такое количество, а споры что лучше или хуже не утехают. Но теория, как Вы знаете, без практики мертва. И всё надо проверять в "железе". По тому мы здесь и собрались, чтоб не останавливаться на достигнутом, а обмениваться опытом, вопрошать, советовать, искать решения, алгоритмы. Всё для этого есть.

Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник


Пост N: 191
Зарегистрирован: 01.03.11
Рейтинг: 2
ссылка на сообщение  Отправлено: 05.02.13 14:30. Заголовок: Александр пишет: на..


Александр пишет:

 цитата:
на самом деле могу сказать от себя что больше мне помог самоучитель 2

Как бы этот самоучитель не сыграл злую шутку... Все что там написано просто противоречит здравому смыслу, в смысле оформления и лирических отступлений. Лично мне очень было сложно даже читать чужие программы. Сейчас мне стало куда проще, даже переход с piс контроллеров на STM32 стал просто детской игрой... По сути пара дней и контроллер был уже запущен, а это основа. Осталось просто изучать работу его модулей, что тоже не составило особых затруднений пока... По сути стоит относится серьезно только к первой части самоучителя, остальное следует принимать только к сведению или как помощь, но ни в коем случае как образец для подражания... Сейчас даже многие птенцы до сих пор не умеют пользоваться файлами с описание стандартных регистров, не пользуются директивой cblock и многим другим. Вы попробуйте из первой части самоучителя подключить #include <p16F84A> и убрать назначение адресов стандартным регистрам специального назначения типа STATUS и увидите что программа даже не скомпилируется... Всему виной нестандартное оформление программы. Даже сейчас еще не все исключенные переучились на стандартное оформление программ, до сих пор используют вместо bsf STATUS,RP0 запись bsа STATUS,5 не смотря на то, что первая запись значительно понятнее...

Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 609
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 05.02.13 15:21. Заголовок: Алексей пишет: Даже..


Алексей пишет:

 цитата:
Даже сейчас еще не все исключенные переучились на стандартное оформление программ, до сих пор используют вместо bsf STATUS,RP0 запись bsа STATUS,5 не смотря на то, что первая запись значительно понятнее...


Наверное bsf STATUS,5.
Согласен, лучше привыкать сразу к надлежащему написанию, тем более это и самому полезнее, так как в мозгах фиксируется правильное название бита в регистре, а не его местоположение. Но мне , допустим, иногда яснее использовать именно второй вариант, потому как чаще и быстрее запоминается именно положение бита в регистре, а не его название. Тем более названия бывают очень созвучны и можно ошибиться. Но в любом случае полезно даже для самого себя в коментах фиксировать действие, зачем используется та или иная команда или блок команд.
А вот для чего прописывать (назначать) адреса стандартным спец. регистрам, я так и не понял. Что это даёт? При отладке их можно всегда вывести в окне, там и адреса их есть. А подключив инструкцию МК они и так сами определяются. Ни когда не прописывал их. Очень полезная вещь от Tester есть по оформлению программ, иногда перечитываю.

Спасибо: 0 
ПрофильЦитата Ответить
moderator




Пост N: 584
Зарегистрирован: 01.03.11
Рейтинг: 3
ссылка на сообщение  Отправлено: 05.02.13 19:00. Заголовок: не bcf STATUS,5 и b..


не bcf STATUS,5 и bcf STATUS,RP0 это все неправильно по сути.
Правильно banksel, для совместимости и переносимости.
Хотя по сути, все будет работать. На мой взгляд не страшно. Заморачивать себе голову на этом не надо. Каждый придет к удобному для себя варианту


Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 610
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 06.02.13 10:43. Заголовок: Ну вот и разрулил! :..


Ну вот и разрулил!

Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 611
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 10.02.13 22:05. Заголовок: Кстати, на счёт bank..


Кстати, на счёт banksel.
Если необходимо произвести действие над несколькими спец регистрами, как то не удобно перед каждым выбор банка ставить. И потом, после действия над регистром, как знать в каком банке ты находишься? Лично мне ближе принудительный выбор, как то надёжнее. И через силу бит названием прописываю, типа так правильнее, хороший тон. Цифрой быстрее, понятнее и удобнее, ИМХО! Ведь когда инициализируешь МК, удобнее бинарным способом регистры прописывать. Тут и место бита видно, и менять легко.

Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник


Пост N: 192
Зарегистрирован: 01.03.11
Рейтинг: 2
ссылка на сообщение  Отправлено: 12.02.13 11:21. Заголовок: SanSanich пишет: Ц..


SanSanich пишет:

 цитата:
Цифрой быстрее, понятнее и удобнее, ИМХО!

В том то и дело что нет. Это ты сейчас знаешь каждый номер бита наизусть. А теперь представь что этих битов сотни и разбросаны по десяткам регистров. Когда ты работаешь с PIC12 или PIC16 их немного, и запомнить их можно в принципе. Но вот если у тебя был перерыв в работе? Ну предположим месяц? вспомнишь ли ты все биты по номерам? Очень сомневаюсь. Вот давай к примеру вспомни какой бит отвечает за включение таймера 1 в PIC16F628A. Только по честному, не заглядывая ни в даташит, ни в свои ни в чужие исходники, ни куда либо еще кроме своей памяти... Я допустим напишу просто
bsf T1CON, TMR1ON

Тут даже коментария не нужно, и так прекрасно видно что этой строкой включается тмр1.
Или же например надо включить прерывания и разрешить прерывание по INT0. Если написать так
 
movlw (1<<GIE | 1<<INTE)
movwf INTCON

Сразу видно что делаешь этой строкой и без комментариев и без заглядывания в даташит... А теперь попробуй тоже самое написать пользуясь только своей памятью... А потом в ходе отладки программы все это еще и помнить. А представь что программа у тебя сразу не заработала и ты полез проверять правильно ли ты задал маску? Немудрено и ошибиться... А запись здесь расшифровывается очень просто, 1 сдвинуть влево на константу GIE, т.е. 7 раз влево. Вот и получим старший бит под №7 в маске. Знак | всего лишь означает логическую операцию ИЛИ. Таким образом и формируем маску для регистра INTCON. Теперь предположим что в ходе написания программы тебе понадобилось включить еще прерывания по изменению уровня сигнала на порту В и ты не помнишь номер бита... Лезем в даташит ищем нужный номер бита и переставляем в маске нужный битик... Пользуясь константами это можно сделать даже не заглядывая в даташит
 
movlw (1<<GIE | 1<<INTE | 1<<RBIE)
movwf INTCON

Просто добавив еще одну запись 1<<RBIE. Маска формируется автоматически и сразу с первого взгляда видно что она делает. Я сразу смогу сказать что данной маской разрешаются все прерывания, включается прерывание по INT0 и по изменению уровня на порте В... Кстати, для ярых противников Си. Си вообще здесь не при чем, это голый асм...
Таким же макаром очень удобно сбрасывать флаги, т.к. не нужно помнить номера битов
 
bcf INTCON, INTF
и
bcf INTCON, RBIF

И при первом же беглом просмотре чужой программы видно сразу что мы делаем. Да даже при просмотре своей собственной программы при отладке уже не нужно держать в памяти что делается данными строками. Да, я помню, какой был гемор, когда писал тоже только номера битов. Сколько мозг поломаешь пока отлаживаешь. Как только начал пользоваться константами текст программы стал в разы легче читаться... Даже если в программе мне нужны флаги, ну например 6 штук, я для них стараюсь объявлять символьные имена. Например объявляю регистр Flag а для его же битов объявляю константы
 
#define fl1 0
#define fl2 1
#define fl3 2
#define fl4 3
#define fl5 4
#define fl6 5

и пишу уже так bcf Flag, fl2 и при отладке программы я уже знаю какой флаг сбросил. А удержать в памяти каждый бит регистра Flag тяжеловато, постоянно путаешься... естественно fl2 определение только условно для примера, в реальности каждому биту даю осмысленные названия, соответствующие смыслу этого флага.
Мне лично непонятна такая вот тяга в программе писать сами номера битов вместо стандартных предопределенных для них констант . Сан Саныч, вот смотри когда ты показываешь свои программы с какими то циферками, обозначающих номер бита, лично мне крайне тяжело так посмотреть. Тот же банк переключить... RP0 и RP1 это понятно, а какие биты действительно за это отвечают в регистре STATUS я уже точно и не помню... Поэтому запись STATUS,5 мне вообще ни о чем не говорит... А все это школа КЕА... Надо отвыкать уже. Обязательно нужно пользоваться константами и не заниматься мазохизмом, тем более на асме, где и так хватает забот .
Понятнее как раз не цифрой, а стандартной константой, которая уже определена за нас. Нам остается только пользоваться... Насчет быстрее тоже можно поспорить, учитывая время на ползание по даташиту в поисках нужного бита. С удобнее я бы тоже поспорил, особенно при отладке программы, когда видишь только номера битов а не их символьные имена... пока вспомнишь что это за бит такой... мозг сломаешь...

Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 612
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 12.02.13 13:42. Заголовок: Алексей Сдаюсь! Уло..


Алексей
Сдаюсь! Уложил на лопатки! Почему?
Буквально вчера на свои же грабли и наступил. Объявил АЦП, настроил
		movlw			B'1000001'		; Правое выравнивание, Vdd, AN0,АЦП включен 
movwf ADCON0

и мучаюсь в протеусе, ругается гад и всё!
Потом углядел, НОЛИК пропустил, мать его!
		movlw			B'10000001'		; Правое выравнивание, Vdd, AN0,АЦП включен 
movwf ADCON0

Вся беда в том, что названия битов, регистров, кто за что, пока в голове не задерживаются. Но посидев 2 недели с мплабом и ДШ под подушкой, кое что проясняется. Не знал о таком приёме
 movlw (1<<GIE | 1<<INTE | 1<<RBIE)  
movwf INTCON

Не важна очерёдность? Очень удобно и видно сразу.
Всё, буду причёсывать свою новую прогу!
Ещё вопрос, константы в каком лучше виде. Мне удобнее в десятичном, типа .120 . К адресам не относится, просто число в регистре.
Была здесь хорошая тема "Систематизация кода и оформление программ", так кажется называлась. Но в пылу боя она сгорела!
Может возобновить? Довольно часто возникают вопросы и по оформлению, и по алгоритмам, стандартным процедурам и тд. Можно было бы делиться своими наработками в этом вопросе. Я её создам, а там видно будет!
Алексей

Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник


Пост N: 193
Зарегистрирован: 01.03.11
Рейтинг: 2
ссылка на сообщение  Отправлено: 12.02.13 14:26. Заголовок: SanSanich пишет: и ..


SanSanich пишет:

 цитата:
и мучаюсь в протеусе, ругается гад и всё!
Потом углядел, НОЛИК пропустил, мать его!

А ты знаешь сколько я раз на такие грабли наступал? и на подобные...
SanSanich пишет:

 цитата:
Не важна очерёдность?

Не важна... Сам посуди... эта запись будет абсолютна эквивалентна вот такой
(b'10000000' ИЛИ b'00010000' ИЛИ b'00001000') = b'10011000'
теперь если заглянешь в дизасм этой строки то увидишь
  
movlw b'10011000'
movwf INTCON
именно это, в какой бы последовательности биты ты не поставил. Т.е. запись маски через константы мало того что наглядно видно что за маска задается, чтобы ошибиться надо еще очень сильно постараться, так еще и абсолютна эквивалентна и не создает лишних команд...
SanSanich пишет:

 цитата:
Ещё вопрос, константы в каком лучше виде.

Все зависит от задачи, которую преследует константа... В дизасемблере все равно все будет в двоичном виде... Например перекодировку из двоично десятичных чисел в код семисегментного индикатора удобно задавать в двоичном виде, константы в счетчике в десятичном, маски задавать удобно опять в двоичном. в шестнадцатиричном удобно байты в массив укладывать. Вобщем все зависит от задачи. допустим чтобы задать маску когда ты знаешь какие номера битов нужно считать а которые обнулить, например так b'00110101' (Ну или лучше воспользоваться либо стандартными константами, или сразу определить из самому), сразу в двоичном можно задать чтобы не переводить в другую систему, просто смысла нет... но можно и перевести, но тогда если будешь править программу потеряешь наглядность, например при переключении ножек порта... Опять придется переводить и туда и сюда... Если организовываешь счетчик, то естественно зачем мозг ломать другими системами, если сразу знаешь что счетчик должен отсчитать ровно .120 раз? Естетсвенно что константу лучше записать сразу в десятичном виде.
Вот еще пример. Ты подключаешь например LCD к контроллеру и работаешь с ним в программе так
 
bsf PORTA,0 ; Формирование импульса строба на линии Е
bcf PORTA,0

А теперь ситуация. Начал разводить печатную плату и все у тебя очень хорошо, красиво получается, НО! линии E и А0 у тебя пересекаются и никак не разводятся по другому. Решение простое, как дважды два, поменять местами эти две ножки на плате... В программе их тоже надо перекинуть. Хорошо если у тебя формирование строба только в одном месте и надо только подправить эти две команды, а также формирование А0, а вот его уже точно будет больше! и ты начинаешь шерстить всю программу чтобы подменить, и можешь сделать еще кучу ошибок... Поэтому лично я поступаю так
 
#define E PORTA,RA0
#define A0 PORTA,RA1

А в программе просто пишешь уже
 
bsf E
bcf E

и так же с А0... Теперь тебе достаточно поменять их местами в шапке, а все остальное сделает компилятор. Для задания направления работы этих самых ножек поступаю точно таким же образом.

Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 615
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 12.02.13 16:15. Заголовок: Не удержался, уж бол..


Не удержался, уж больно мне нравится пример оформления программы у В. Тимофеева.
При желании можно воспользоваться готовыми блоками, адаптируя их под свои нужды.
;*******************************************************************************  
; voltmetr.asm
;
; Одноканальный вольтметр 0..Vdd с выводом на 3х позиционный 7-сегментный индикатор.
;
; RA0 - аналоговый вход
; RC5, RC6, RC7 - общие катоды сегментов
; portb - управление сегментами
;
; Программа является примером, поясняющим правила оформления, описанных в статье:
; "MPASM: как правильно оформлять программы на ассемблере для PIC-контроллеров"
;
; Автор: В.Тимофеев, testerplus@mail.ru, osa@pic24.ru
;
; История: 13.11.2010 - Файл создан
;
;*******************************************************************************

list p=16f873a
include <p16f873a.inc>

__CONFIG _HS_OSC & _CP_ALL & _LVP_OFF & _DEBUG_OFF & _WDT_ON & _BODEN_OFF

radix dec

ERRORLEVEL -302


;*******************************************************************************
;
; КОНСТАНТЫ ПОЛЬЗОВАТЕЛЯ (могут меняться в зависимости от режима)
;
;*******************************************************************************

F_OSC equ .4000000 ; Тактовая частота (Гц)
F_ADC equ .1000 ; Частота измерений ADC
; Для отображения на индикаторе производится
; осреднение по 128 выборкам

;-------------------------------------------------------------------------------
; Параметры входного сигнала
;-------------------------------------------------------------------------------
V_REF equ .5000 ; Задаем напряжение опоры (мВ)
ADC_RESOLUTION equ .10 ; Разрешение АЦП
AVERAGE_CONST equ .16-ADC_RESOLUTION ; Константа осреднения будет зависеть от
; разрядности АЦП с тем, чтобы сумма
; измерений четко уложилась в два байта

;*******************************************************************************
;
; КОНСТАНТЫ РАБОЧИЕ
;
;*******************************************************************************

;-------------------------------------------------------------------------------
; Порты ввода вывода
;-------------------------------------------------------------------------------
TRISA_CONST equ b'000001' ; AN0 - вход
PORTA_CONST equ b'000000'

TRISB_CONST equ b'00000000' ; Все выходы
PORTB_CONST equ b'11111111' ; Управление сегментами

SEG_A equ (1<<0) ; Назначение сегментов в индикаторах
SEG_B equ (1<<1) ;
SEG_C equ (1<<2) ; A
SEG_D equ (1<<3) ; F B
SEG_E equ (1<<4) ; G
SEG_F equ (1<<5) ; E C
SEG_G equ (1<<6) ; D H
SEG_H equ (1<<7) ;

TRISC_CONST equ b'00000000' ; Все выходы
PORTC_CONST equ b'00000000'

#define PIN_DIGIT_0 PORTC, 5
#define PIN_DIGIT_1 PORTC, 6
#define PIN_DIGIT_2 PORTC, 7
;-------------------------------------------------------------------------------
; Параметры АЦП
;-------------------------------------------------------------------------------
ADCON1_PCFG_CONST equ b'1110' ; RA0 - аналоговый, остальные - цифровые

;-------------------------------------------------------------------------------
; Параметры TMR0
;-------------------------------------------------------------------------------
TMR0_PERIOD equ F_OSC/4/F_ADC
TMR0_PRS_CONST equ 0x01
TMR0_CONST equ TMR0_PERIOD / (2 << TMR0_PRS_CONST)


; Проверка, что получили константу, соответствующую разрядности TMR8 (не более 8 бит)
#if TMR0_CONST >= 256
error Неправильно выбрана константа TMR0_PRS_CONTS! Следует увеличить ее значение!
#endif



;*******************************************************************************
;
; ПЕРЕМЕННЫЕ
;
;*******************************************************************************

CBLOCK 0x20
output_text :3 ; Текстовое представление напряжения
cur_digit ; Текущая цифра для отображения

work_data :3 ; Для преобразования из единиц АЦП в милливольты
adc_average_data :2 ; Результат АЦП
adc_average_counter ; Счетчик измерений при усреднении (см. ADC_AVERAGE)

product :3 ; Переменные для подпрограммы умножения
multiplier :2

divider ; Переменная для подпрограммы деления со сдвигом

div_result ; Переменные для bin2dec преобразования

ENDC
;-------------------------------------------------------------------------------

CBLOCK 0x70
wreg_temp ; Регистры для сохранения контекста
status_temp
fsr_temp
pclath_temp
ENDC


;*******************************************************************************
;
; ВЕКТОР СБРОСА
;
;*******************************************************************************

ORG 0x0000
lgoto Start


;*******************************************************************************
;
; ПРЕРЫВАНИЕ
;
;*******************************************************************************
ORG 0x0004
Interrupt:
movwf wreg_temp
movf status, w
movfw status_temp
movf pclath, w
movwf pclath_temp
movf fsr, w
movwf fsr_temp
clrf status
clrf pclath

;-------------------------------------------------------------------------------
; Прерывание TMR0: смена текущей позиции индикатора
;-------------------------------------------------------------------------------
T0IF_Check:

btfsc intcon, T0IF
btfss intcon, T0IE
goto T0IF_Skip
;-----------------------------------------------------------------------
bcf intcon, T0IF ; Обновляем таймер
movlw -TMR0_CONST ;
addwf TMR0, f ;
;-----------------------------------------------------------------------

btfss adc_average_counter, AVERAGE_CONST
; Пропускаем измерение, если основная программа
; еще не обработала данные
bsf adcon0, GO ; Запускаем следующее преобразование

bcf PIN_DIGIT_0 ; Сначала гасим все три индикатора
bcf PIN_DIGIT_1 ;
bcf PIN_DIGIT_2 ;
clrf portb ;
;-----------------------------------------------------------------------
movf cur_digit, w ; Включаем один из сегментов
btfsc status, Z ;
bsf PIN_DIGIT_0 ;
btfsc cur_digit, 0 ;
bsf PIN_DIGIT_1 ;
btfsc cur_digit, 1 ;
bsf PIN_DIGIT_2 ;
;-----------------------------------------------------------------------
addlw output_text ; Зажигаем нужные сегменты
movwf fsr ; fsr = адрес цифры для вывода
movf indf, w ;
movwf portb ;
;-----------------------------------------------------------------------
incf cur_digit, f ; Берем следующую позицию (0,1,2,0,1,2,0,...)
movf cur_digit, w ;
xorlw 0x3
btfsc status, Z ;
clrf cur_digit ;

T0IF_Skip:


;-------------------------------------------------------------------------------
; Прерывание АЦП: завершение АЦ-преобразования
;-------------------------------------------------------------------------------


ADIF_Check:

banksel pie1 ; Пропускаем, если прерывание запрещено
btfss pie1, ADIE ; или не завершено
goto ADIF_Skip ;
banksel pir1 ;
btfss pir1, ADIF ;
goto ADIF_Skip ;
;-----------------------------------------------------------------------
bcf pir1, ADIF
;-----------------------------------------------------------------------
; Условия сброса сторожевого таймера:
; 1. Прерываня T0IF и ADIF возникают и
; обрабатываются не реже, чем раз в 18 мс
clrwdt ; 2. Основная программа периодически получает
; управление (т.к. adc_average_counter обнуляется)
; Следовательно, если попали в эту точку, значит,
; программа работает в штатном режиме
;-------------------------------;

banksel adresl ; Выполняем сложение:
movf adresl, w ; adc_average_data = adc_average_data + ADRES
banksel adresh ;
addwf adc_average_data, f ;
movf adresh, w ;
btfsc status, c ;
incf adresh, w ;
addwf adc_average_data+1, f ;
incf adc_average_counter, f ; Увеличиваем счетчик выборок
btfss adc_average_counter, AVERAGE_CONST ; Собрали ли нужное количество выборок?
goto ADIF_Skip ; Еще нет.

movf adc_average_data, w ; Если собрали, то переносим данные в рабочую
movwf work_data ; переменную: work_data = adc_average_data
movf adc_average_data+1, w ;
movwf work_data+1 ;
clrf work_data+2 ;

clrf adc_average_data ; Готовим переменную к сбору следующих выборок
clrf adc_average_data + 1
;-----------------------------------------------------------------------
ADIF_Skip:
clrf status

;-------------------------------------------------------------------------------
Int_Exit:
movf fsr_temp, w
movwf fsr
movf pclath_temp, w
movwf pclath
movf status_temp, w
movfw status
swapf wreg_temp, f
swapf wreg_temp, w
retfie


;*******************************************************************************
;
; ИНИЦИАЛИЗАЦИЯ
;
;*******************************************************************************
Start:
clrf status
clrf intcon
;----------------------------------------------------------------------
; Настройка цифровых портов
;----------------------------------------------------------------------
movlw PORTA_CONST
movwf porta
movlw PORTB_CONST
movwf portb
movlw PORTC_CONST
movwf portc

banksel trisa
movlw TRISA_CONST
movwf trisa
movlw TRISB_CONST
movwf trisb
movlw TRISC_CONST
movwf trisc
;----------------------------------------------------------------------
; Настройка аналогового входа
;----------------------------------------------------------------------
banksel adcon1
movlw ADCON1_PCFG_CONST
movwf adcon1
bsf adcon1, ADFM ; Правое выравнивание

banksel adcon0
clrf adcon0 ; Выбран канал 0 (AN0)
bsf adcon0, ADON ; Включить модуль АЦП
bsf adcon0, ADCS1 ; Clock = Fosc/32

;----------------------------------------------------------------------
; Настройка таймера
;----------------------------------------------------------------------

banksel option_reg
movlw TMR0_PRS_CONST
movwf option_reg

banksel tmr0
movlw -TMR0_CONST
movwf tmr0
;----------------------------------------------------------------------
; Настройка прерываний
;----------------------------------------------------------------------

bsf intcon, T0IE
banksel pie1
clrf pie1
bsf pie1, ADIE

BSF intcon, PEIE
BSF intcon, GIE
clrf STATUS








;*******************************************************************************
;
; ОСНОВНОЙ ЦИКЛ
;
;*******************************************************************************


clrf adc_average_counter

clrf adc_average_data ; Сумма для осреднения изначально = 0
clrf adc_average_data +1 ;
clrf adc_average_data +2 ;

movlw 8 ; При старте на дисплей выводится 888
call GetSegments ;
movwf output_text ;
movwf output_text+1 ;
movwf output_text+2 ;

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

Main:
btfss adc_average_counter, AVERAGE_CONST
goto Main

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

bcf adc_average_counter, AVERAGE_CONST

;-----------------------------------------------------------------------
; Сейчас в рабочей переменной word_data сумма последних
; (1<<AVERAGE_CONST) измерений
;-----------------------------------------------------------------------

movlw AVERAGE_CONST
lcall DivideShift

;-----------------------------------------------------------------------
; Переводим из единиц АЦП в милливольты:
; work_data = work_data * V_REF/(1<<ADC_RES)
;-----------------------------------------------------------------------
call MultiplyVRef ; Умножить work_data на V_REF
movlw ADC_RESOLUTION
lcall DivideShift ; Делить work_data на (1<<ADC_RESOLUTION)

call Bin2Dec ; Перевести work_data в массив десятичных чисел

lgoto Main

;*******************************************************************************
;
; ФУНКЦИИ
;
;*******************************************************************************




;*******************************************************************************
; MultiplyVRef
;-------------------------------------------------------------------------------
; Функция выполняет перемножение: work_data = work_data * (V_REF/10)
; Т.к. используются только 3 значащие цифры, то единицы мВ не нужны,
; поэтому пересчет ведется в измерении 10мВ
;
; Использует переменные:
; multiplier[2] - второй множитель
; product[3] - произведение
; На выходе:
; work_data = product
;-------------------------------------------------------------------------------
MultiplyVRef:
clrf product ; Обнуляем произведение
clrf product+1 ;
clrf product+2 ;
clrf product+3 ;

movlw high(V_REF/.10) ; Готовим второй множитель
movwf multiplier+1 ;
movlw low(V_REF/.10) ;
movwf multiplier+0 ;

;-----------------------------------------------------------------------
MV_Loop:
btfss multiplier, 0 ; Если очередной бит
goto MV_SkipSumm ; второго множителя установлен,

movf work_data, w ; то выполняем сложение двух 3-байтовых
addwf product, f ; чисел
movf work_data+1, w ;
btfsc status, C ;
incfsz work_data+1, w ;
addwf product +1, f ;
movf work_data+2, w ;
btfsc status, C ;
incfsz work_data+2, w ;
addwf product +2, f ;

MV_SkipSumm:

bcf status, C ; Увеличиваем вес слагаемого
rlf work_data, f ;
rlf work_data+1, f ;
rlf work_data+2, f ;

bcf status, C ; Берем следующий разряд
rrf multiplier+1, f ;
rrf multiplier, f ;

movf multiplier, w ; Прекращаем вычисления, когда заканчиваются
iorwf multiplier+1, w ; биты во втором множителе
btfss status, Z ;
goto MV_Loop ;
;-----------------------------------------------------------------------

movf product, w ; Копируем произведение в рабочую переменную
movwf work_data ;
movf product+1, w ;
movwf work_data+1 ;
movf product+2, w ;
movwf work_data+2 ;

return ; MultiplyVRef

;*******************************************************************************
; DivideShift
;-------------------------------------------------------------------------------
; Функция выполняет деление work_data на(1<<WREG) по правилам целочисленного деления:
;
; X/Y -> (X + Y/2) / Y
;
; Использует перемнные:
; divider - счетчик цикла сдвигов
; На входе:
; work_data - делимое
; WREG - степень делителя (1 << WREG)
; На выходе:
; work_data
;
;-------------------------------------------------------------------------------
DivideShift:
movwf divider
DS_Loop:
bcf status, C ; Выполняем нужное количество сдвигов,
rrf work_data+2, f ; т.е. эквивалент деления на (1 << divider)
rrf work_data+1, f
rrf work_data+0, f
decfsz divider, f
goto DS_Loop

;-----------------------------------------------------------------------
; На данный момент CARRY=1, если остаток больше половины делителя
; в этом случае нужно прибавить к частному "1"
;-----------------------------------------------------------------------

btfsc status, C
incfsz work_data+0, f
return
incfsz work_data+1, f
return
incf work_data+2, f
return

; DivideShift





;*******************************************************************************
; Bin2Dec
;-------------------------------------------------------------------------------
; Функция переводит переменную work_data в массив комбинаций для вывода на
; 7-сегментный индиктор
;
; На выходе:
; output_text
;-------------------------------------------------------------------------------
Bin2Dec:

movlw .100
call DivWreg ; wreg = сотни, work_data = work_data % 100
call GetSegments ; Получаем соответсвующую комбинацию сегментов
addlw SEG_H ; У первого разряда зажигаем точку
movwf output_text + 0

movlw .10
call DivWreg ; wreg = десятки, work_data = work_data % 10
call GetSegments ; Получаем соответсвующую комбинацию сегментов
movwf output_text + 1

movf work_data, w ; wreg = единицы
call GetSegments ; Получаем соответсвующую комбинацию сегментов
movwf output_text+2

return ; Bin2Dec
;*******************************************************************************
; DivWreg
;-------------------------------------------------------------------------------
; Деление двухбайтового числа на wreg методом вычитания
; Использует переменные:
; div_result
; На входе:
; work_data - делимое
; wreg - делитель
; На выходе:
; wreg - частное
; work_data - остаток
;-------------------------------------------------------------------------------
DivWreg:
clrf div_result
DW_Loop:
incf div_result, f ; Увеличиваем счетчик вычитаний
subwf work_data, f ; Производим вычитиние делителя из делимого
btfss status, C ;
decf work_data+1, f ;

btfss work_data+1, 7 ; Проверка на отрицательный результат (бит 15=1)
goto DW_Loop
;-----------------------------------------------------------------------

addwf work_data, f ; Восстанавливаем остаток
incf work_data+1, f ;

decf div_result, w ; Возвращаем частное

return



;*******************************************************************************
; GetSegments
;-------------------------------------------------------------------------------
; Возвращает комбинацию сегментов 7-сегментного индикатора для конкретной цифры
; На входе:
; wreg - чисо от 0 до 9
; На выходе:
; wreg - комбинация сегментов
;-------------------------------------------------------------------------------
ORG 0x0400
GetSegments:
clrf pclath
bsf pclath, 2
andlw 0xF
addwf pcl, f

retlw SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F ; zero
retlw SEG_B + SEG_C ; one
retlw SEG_A + SEG_B + SEG_D + SEG_E + SEG_G ; two
retlw SEG_A + SEG_B + SEG_C + SEG_D + SEG_G ; three
retlw SEG_B + SEG_C + SEG_F + SEG_G ; four
retlw SEG_A + SEG_C + SEG_D + SEG_F + SEG_G ; five
retlw SEG_A + SEG_C + SEG_D + SEG_E + SEG_F + SEG_G ; six
retlw SEG_A + SEG_B + SEG_C ; seven
retlw SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F + SEG_G ; eight
retlw SEG_A + SEG_B + SEG_C + SEG_D + SEG_F + SEG_G ; nine

; Дополняем таблицу до 16 значений, чтобы предотвратить
; улет неизвестно куда при неправильном значении wreg на входе
retlw 0
retlw 0
retlw 0
retlw 0
retlw 0
retlw 0

;*******************************************************************************
;
; Конец программы Voltmetr.asm
;
;*******************************************************************************


Спасибо: 0 
ПрофильЦитата Ответить
moderator




Пост N: 586
Зарегистрирован: 01.03.11
Рейтинг: 3
ссылка на сообщение  Отправлено: 12.02.13 18:49. Заголовок: adc_average_counter ..


adc_average_counter
ADCON1_PCFG_CONST
adc_average_counter, AVERAGE_CONST

Очень красиво. Ага.
Насколько помню Тестер тоже признал, что увлекся

Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 616
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 12.02.13 18:58. Заголовок: MAZ пишет: adc_aver..


MAZ пишет:

 цитата:
adc_average_counter
ADCON1_PCFG_CONST
adc_average_counter, AVERAGE_CONST


А что это означает? Расшифруйте, please!
Да, некоторые вещи не совсем понятны и не популярны, но всё же?

Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник


Пост N: 195
Зарегистрирован: 01.03.11
Рейтинг: 2
ссылка на сообщение  Отправлено: 13.02.13 09:23. Заголовок: MAZ пишет: Очень кр..


MAZ пишет:

 цитата:
Очень красиво. Ага.

Сашь. Это все константы... ты их можешь назначить хоть из трех букв, главное чтобы было читабельно...

Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник


Пост N: 196
Зарегистрирован: 01.03.11
Рейтинг: 2
ссылка на сообщение  Отправлено: 13.02.13 09:30. Заголовок: ADC_RESOLUTION equ..


  ADC_RESOLUTION  equ     .10                ; Разрешение АЦП   
AVERAGE_CONST equ .16-ADC_RESOLUTION ; Константа осреднения будет зависеть от
; разрядности АЦП с тем, чтобы сумма
; измерений четко уложилась в два байта
adc_average_counter ; Счетчик измерений при усреднении (см. ADC_AVERAGE)
ADCON1_PCFG_CONST equ b'1110' ; RA0 - аналоговый, остальные - цифровые

Вот, просто взял из шапки. Т.е. в программе вместо символьных имен констант будет поставлены их значения, присвоенные в шапке... А adc_average_counter это просто регистр общего назначения...

Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 617
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 13.02.13 12:17. Заголовок: Алексей Не досмотре..


Алексей
Не досмотрел, точно.
Есть у меня в проге инициализация, сделанная в цифрах. Хочу попробовать как ты подсказал.
Вот первый вариант
Init 
clrf STATUS
clrf GPIO ; Инициализация защелок GPIO на выход
movlw B'00000111' ; Компаратор выключен
movwf CMCON ; Регистр компаратора
bsf STATUS,5
movlw B'00000101' ; Каналы GP2,GP0(5,7 нога)- выход, остальные – цифровые входы
movwf TRISIO
movlw B'00110001' ; AD clock internal rc, выбор АЦП0 (7 нога)
movwf ANSEL
movlw B'00000000' ; Подтягивающие резисторы выключены
movwf WPU
movlw B'01001111' ; WDT 1:256, по переднему фронту,INTEDG(OPTION_REG<6>)
movwf OPTION_REG
movlw B'10010000' ; Разрешить все не маскированные прерывания, на входе GP2/INT
movwf INTCON
bcf STATUS,5
movlw B'10000001' ; Правое выравнивание, Vdd, AN0,АЦП включен
movwf ADCON0

А вот как у тебя, типа
 movlw (1<<GIE | 1<<INTE) 
movwf INTCON

не получается.



Спасибо: 0 
ПрофильЦитата Ответить
moderator




Пост N: 587
Зарегистрирован: 01.03.11
Рейтинг: 3
ссылка на сообщение  Отправлено: 13.02.13 13:14. Заголовок: Алексей пишет: Сашь..


Алексей пишет:

 цитата:
Сашь. Это все константы...


Я понимаю. Но как они интересно записаны. И очень читаемы



Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 618
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 13.02.13 17:48. Заголовок: Вот ещё ситуация. Ес..


Вот ещё ситуация.
Есть блок, регистры в 1 банке и идут подряд.
Что лучше применить, banksel или вначале и в конце блока STATUS, RP0?
Init 
clrf STATUS
clrf GPIO ; Инициализация защелок GPIO на выход
banksel CMCON
movlw B'00000111' ; Компаратор выключен
movwf CMCON
; bsf STATUS, RP0
banksel TRISIO
movlw B'00101110' ; Каналы GP4,GP0(3,7 нога)- выход, остальные – цифровые входы
movwf TRISIO
banksel WPU
movlw B'00111110' ; Подтягивающие резисторы включены
movwf WPU
banksel OPTION_REG
movlw B'00001111' ; WDT 1:256
movwf OPTION_REG
banksel INTCON
movlw B'10001000' ; Разрешить все немаскированные прерывания, Уровня сигнала на входах
movwf INTCON
banksel IOCB
movlw B'00101110' ; Прерывания на входах
movwf IOCB
; bcf STATUS, RP0

И ещё, как я понял banksel работает ТОЛЬКО на тот регистр, который указан. Затем возвращаемся в 0 банк. Это так?


Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник


Пост N: 197
Зарегистрирован: 01.03.11
Рейтинг: 2
ссылка на сообщение  Отправлено: 13.02.13 18:10. Заголовок: SanSanich пишет: А ..


SanSanich пишет:

 цитата:
А вот как у тебя, типа

movlw (1<<GIE | 1<<INTE)
movwf INTCON


не получается.

Ты уверен что правильно все делаешь? Я специально попробовал, думал может чего с синтаксисом, все норма, работает... константа пишется в регистр... Код выложи на чем пробуешь... У меня мплаб 8,63.
SanSanich пишет:

 цитата:
banksel или вначале и в конце блока STATUS, RP0

в принципе можно и так и так, но лучше banksel. Это просто встроенный макрос, который соответственно раскладывается на
 
bsf STATUS,RP0
bcf STATUS,RP1

комбинации bsf и bcf могут меняться в зависимости от банка, но обязательно будут присутствовать эти две команды. Макросу вообще все равно какой выбран текущий банк. Хоть вторая команда при переходе из 0 банка в принципе лишняя, но при применении banksel все равно она будет присутствовать. Естественно банк будет выбран до тех пор, пока ты его снова не переключишь в программе...

Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 619
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 13.02.13 18:34. Заголовок: Алексей пишет: Есте..


Алексей пишет:

 цитата:
Естественно банк будет выбран до тех пор, пока ты его снова не переключишь в программе...


Т.е. если я находясь в 0 банке обратился в другой к регистру через banksel, и не вернулся после этого обратно, продолжая работать с регистрами 0 банка, будет ошибка? Это что ж к каждому регистру надо через banksel обращаться?
Вот на счёт
Алексей пишет:

 цитата:
Код выложи на чем пробуешь...


Init  
clrf STATUS
clrf GPIO ; Инициализация защелок GPIO на выход
movlw B'00000111' ; Компаратор выключен
movwf CMCON ; Регистр компаратора
bsf STATUS,5
movlw B'00000101' ; Каналы GP2,GP0(5,7 нога)- выход, остальные – цифровые входы
movwf TRISIO
movlw B'00110001' ; AD clock internal rc, выбор АЦП0 (7 нога)
movwf ANSEL
movlw B'00000000' ; Подтягивающие резисторы выключены
movwf WPU
movlw B'01001111' ; WDT 1:256, по переднему фронту,INTEDG(OPTION_REG<6>)
movwf OPTION_REG
movlw B'10010000' ; Разрешить все не маскированные прерывания, на входе GP2/INT
movwf INTCON
bcf STATUS,5
movlw B'10000001' ; Правое выравнивание, Vdd, AN0,АЦП включен
movwf ADCON0

Бинарные надо заменить, если не трудно, смени парочку а я проверю. Я взял твой пример и вставил, лаб заругался на синтаксис. MPLAB IDE Version 8.30.00.00

Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник


Пост N: 198
Зарегистрирован: 01.03.11
Рейтинг: 2
ссылка на сообщение  Отправлено: 13.02.13 18:53. Заголовок: SanSanich пишет: Я..


SanSanich пишет:

 цитата:
Я взял твой пример и вставил, лаб заругался на синтаксис.

Не должен, чего хоть пишет?
SanSanich пишет:

 цитата:
смени парочку а я проверю.

Вот:
 
movlw (1<<TRIS2 | 1<<TRIS0) ; Каналы GP2,GP0(5,7 нога)- выход, остальные – цифровые входы
movwf TRISIO
movlw (1<<ADFM | 1<<ADON) ; Правое выравнивание, Vdd, AN0,АЦП включен
movwf ADCON0


Спасибо: 0 
ПрофильЦитата Ответить
администратор




Пост N: 620
Зарегистрирован: 16.03.09
Откуда: Родина, Севастополь
Рейтинг: 1
ссылка на сообщение  Отправлено: 13.02.13 22:15. Заголовок: Вторая прошла, перва..


Вторая прошла, первая не хотит
   		movlw		(1<<TRIS2 | 1<<TRIS0)		; Каналы GP2,GP0(5,7 нога)- выход, остальные – цифровые входы    
movwf TRISIO

Вот что пишет

Release build of project `D:\!PROJECT\!SANICH\Heater_12F675\Heater_12F675.mcp' started.
Language tool versions: MPASMWIN.exe v5.48, mplink.exe v4.46, mplib.exe v4.46
Wed Feb 13 20:52:13 2013
----------------------------------------------------------------------
Clean: Deleting intermediary and output files.
Clean: Done.
Executing: "C:\Program Files (x86)\Microchip\MPASM Suite\MPASMWIN.exe" /q /p12F675 "Heater_12F675.asm" /l"Heater_12F675.lst" /e"Heater_12F675.err" /o"Heater_12F675.o"
Message[312] D:\!PROJECT\!SANICH\HEATER_12F675\HEATER_12F675.ASM 62 : Page or Bank selection not needed for this device. No code generated.
Error[113] D:\!PROJECT\!SANICH\HEATER_12F675\HEATER_12F675.ASM 140 : Symbol not previously defined (TRIS2)
Error[113] D:\!PROJECT\!SANICH\HEATER_12F675\HEATER_12F675.ASM 140 : Symbol not previously defined (TRIS0)
Halting build on first failure as requested.
----------------------------------------------------------------------
Release build of project `D:\!PROJECT\!SANICH\Heater_12F675\Heater_12F675.mcp' failed.
Language tool versions: MPASMWIN.exe v5.48, mplink.exe v4.46, mplib.exe v4.46
Wed Feb 13 20:52:14 2013
----------------------------------------------------------------------
BUILD FAILED

Причём по разному пробовал, и в ручную вводил, и с ДШ копировал, не принимает.


Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник


Пост N: 199
Зарегистрирован: 01.03.11
Рейтинг: 2
ссылка на сообщение  Отправлено: 13.02.13 22:30. Заголовок: Ты подключал файл #i..


Ты подключал файл #include <P12F675.INC>? если да то все должно работать. В любом случае надо смотреть этот файл. Сейчас посмотрел эти константы объявлены так
;----- TRISIO Bits ----------------------------------------------------- 
TRISIO0 EQU H'0000'
TRISIO1 EQU H'0001'
TRISIO2 EQU H'0002'
TRISIO3 EQU H'0003'
TRISIO4 EQU H'0004'
TRISIO5 EQU H'0005'

Так что писать константы нужно так...

Спасибо: 0 
ПрофильЦитата Ответить
Ответ:
1 2 3 4 5 6 7 8 9
видео с youtube.com картинка из интернета картинка с компьютера ссылка файл с компьютера русская клавиатура транслитератор  цитата  кавычки оффтопик свернутый текст

показывать это сообщение только модераторам
не делать ссылки активными
Имя, пароль:      зарегистрироваться    
Тему читают:
- участник сейчас на форуме
- участник вне форума
Все даты в формате GMT  3 час. Хитов сегодня: 2
Права: смайлы да, картинки да, шрифты нет, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет



Создай свой форум на сервисе Borda.ru
Текстовая версия