Новые сообщения в форуме · Участники · Правила форума · Поиск · RSS ]







  • Страница 1 из 1
  • 1
Модератор форума: sanyaav, Volodya215  
Обучение программирования МК (Урок 7, часть2)
sanyaavДата: Пятница, 05.04.2013, 23:09 | Сообщение # 1

Репутация:


Группа:
Журналист


Сообщений: 134
Награды: 8
Статус:Offline
Продолжу рассказ о термометре.
Предыдущую программу индикации дополню работой с АЦП и операциями с числами.
Для начала перекроим шапку программы вот так:


добавляем регистры PIR1 - он покажет момент завершения преобразования в АЦП,
AdresL- хранит младшие 8 бит числа из АЦП,
AdresH - хранит 2 старших бита
ADCON0- регигстр выбора канала для преобразования, а также старт начала преобразования

В регистры общего назначения добавляем:
C_Hold  - для хранения константы времени заряда конденсатора
AC_DC_H  - для копирования старших битов из АЦП
AC_DC_L  - для копирования младших битов из АЦП
minus - регистр в который будем записывать 1 если результат будет отрицательным
Grad  - модуль числа температуры (например -39 по модулю будет 39)



Далее идёт обычная настройка регистров, тут стоит отметить настройку регистра Ansel, 7 бит не используется, биты 6-4 отвечают за источник тактового сигнала (подробнее в даташите), для частоты 4 мгц выбираем Fosc/8 - 001, далее биты 3-0 отвечают за то, какой канал будет работать как АЦП, так как наш термометр будет на 2 датчика, то настраиваем 2 канала: 1 и 0.



На скриншоте выше показана проверка нажатия на кнопку, если нажата, то работаем с 1 каналом, если не нажата, то с 0 каналом. За выбор конкретного канала отвечает регистр ADCON0 биты 3-2 (НЕ ПУТАТЬ ВЫБОР КАНАЛА С НАСТРОЙКОЙ КАНАЛОВ КАК АНАЛОГОВЫЙ ВХОД В РЕГИСТРЕ ANSEL !), так же в этом регистре ставится выравнивание в регистрах AdresH и AdresL битом 7 (подробнее о выравнивании в даташите на странице 43), и выбор источника опорного напряжения, этим источником может быть вывод Vref либо шина питания. ВНИМАНИЕ!!! для упрощения расчётов в данном проекте выбрано питание в 5,12 вольт, для того чтобы изменение напряжения на 0,005 вольта давало изменение ровно на 1 значение в результате преобразования, считать так: 5,12 вольт/1024= 0,005 (забегая вперёд скажу что в протеусе нужно выставить 5,119 вольта на питание, иначе после 62 градусов возникает погрешность в 1 градус.)
Сразу отмечу, что стабильность опорного напряжения очень важна для правильных показаний.



Алгоритм работы с АЦП: подождать заряда конденсатора Сhold, запустить преобразование, подождать завершения преобразования, считать результат.
Для заряда конденсатора есть рекомендованное производителем время (подробнее смотреть на странице 46 даташита), в регистр C_Hold записываем число 33 (число навскидку) - этого хватит с запасом для заряда конденсатора, далее идёт уже известная нам временная задержка. После того как контроллер отсчитает задержку, можно начинать преобразование, для этого сбросим бит 6 в регистре PIR1, этот бит является флагом завершения преобразования (удостовериться можно в даташите на странице 17), затем запускаем преобразование установив бит 1 в регистре ADCON0 в 1 и начинаем проверку бита 6 в регистре PIR1, пока он не установится в 1, крутимся в этом цикле. После того как выставится единица забираем значения из регистров АЦП. ВНИМАНИЕ!!! тут часто возникают ошибки, регистр AdresL находится в 1 банке, а регистр AdresH находится в 0 банке, поэтому сначала лезем в 1 банк забираем значение из младшего регистра, выходим в 0 банк и забираем значение из старшего регистра, переписываем их в регистры общего назначения для дальнейших операций. (внимательно читайте текст и смотрите на регистры, понимание этих моментов очень важно для дальнейшей работы, если что-то непонятно с банками, то возвращайтесь к предыдущим урокам)



Теперь наверное самое сложное в этой программе. Для начала несколько расчётов: из даташита читаем, что при +25 град. напряжение на датчике будет 2,98 (округляется до сотых) +- разброс, этот разброс корректируется дополнительным выводом датчика. Данный датчик измеряет температуру в кельвинах то есть +25 Цельсия - это 298 Кельвина, после преобразования в АЦП будет число 596, для того чтобы его преобразовать в Кельвины, нужно всего навсего выполнить деление на 2, и получим 298, а затем нужно только отнять 273 и получим заветную цифру в Цельсиях. (Вот именно для этого и введено напряжение 5,12, если бы было напряжение 5,0 то делить бы пришлось не на 2, а на 2,048, а это не так то и просто, вариант "развития событий" при 5 вольтах: умножить полученное в АЦП значение на 1000 и делить уже на 2048 или использовать вычисляемый переход для установки фиксированных значений, в этом случае можно уже обойтись без перевода в 10-систему, числа будут "готовые")

Пробежимся по командам: rrf выполняет сдвиг, в данном случае так реализовано деление на 2, (кто не верит запишите например число 10010001 сдвиньте вправо, в результате в этом регистре останется 01001000, а единица будет во флаге С, в качестве остатка).Затем выполняем вычитание, разрядности 8 битного регистра не хватает для числа 273, выполним 2 вычитания: -255 и -18.

Теперь всем ВНИМАНИЕ!!! работаем с флагом С. Когда вычитаемое больше уменьшаемого, то разность получится отрицательной, в микроконтроллере это отслеживается через флаг С, до начала вычитания ставим его в 1, если разность отрицательная, то этот флаг сбросится, если же результат положительный, то так и останется 1. С суммированием всё наоборот, флаг изначально нужно сбрасывать в 0, если сложить 2 числа и результат будет больше 255 (вместимость 1 регистра), то этот флаг установися в 1.
Рассказал, теперь действуем:

bsf Status,0
movlw .255
subwf AC_DC_L,1
btfss Status,0

последней командой проверяем флаг С, если он равен 0, то выполняем команду

goto    DEC_H

То есть идём в старший регистр для вычитания единицы.
Если же переноса не было, то выполняем дальнейшее вычитание -18
пройдя по метке  DEC_H программа выполнит следущие действия:

bsf Status,0
movlw .1
subwf AC_DC_H,1
btfsc Status,0

вычитание 1 из старшего регистра, если был перенос, то результат однозначно отрицательный, если переноса не было, то знак результата будет определён позднее при переходе на метку M18    . (можно заметить, что я выполнял вычитание, а не декремент, как оказалось декремент не влияет на флаг С)

Теперь об отрицательных числах в микроконтоллере. В десятичной системе всё просто: допустим у нас изначально было число 100, мы вычитаем из него 255 и должны получить -155, в микроконтроллере же получим 101, это число в дополнительном коде, чтобы получить из него нормальное число нужно инвертировать биты и прибавить 1: 101 в десятичной = 01100101 в двоичной, инвертируем: 10011010, +1=10011011 переведём в 10 получим 155, и надо помнить, что этот результат с минусом, этот минус мы выставляем в 0-бите региста minus, затем нам нужно вычитать 18, но у нас же 2 отрицательных числа, значит складываем, а минус так и остаётся сохранённым в регистре и переходим по метке DEL.
всё что я выше рассказал записано в командах (кому непонятно-лезем в даташит, смотрим разъяснение команд):

bsf          minus,0
comf       AC_DC_L,1
incf         AC_DC_L,1
movlw    .18
addwf      AC_DC_L,1
goto        DEL

Если же программа перескочила по метке М18, то выполняем следущее:

M18
bsf           Status,0
movlw     .18
subwf      AC_DC_L,1
btfsc        Status,0
goto        DEL
bsf          minus,0
comf       AC_DC_L,1
incf         AC_DC_L,1
goto        DEL
сначала вычитаем 18, затем проверяем флаг С, если не было переноса, то сразу переходим по метке DEL, если эе был перенос, то переводим число из дополнительного кода в прямой, сохраняем знак в регистре minus и переходим по метке DEL.
В результате искомое число градусов будет записаное в регистре AC_DC_L в двоичном коде.



Для вычисления количества десятков и единиц я не стал мудрить и просто вычитал по 1 из числа градусов, проверяя на равенство 10 (эта ветка у меня скопирована из другой простой программы, где тоже не важно время выполнения, если возникнет потребность выполнять вычисления с огромными числами, то нужно будет пользоваться вычитанием десятков, сотен, тысяч... и тд. с проверкой флага Z)

Для начала обнулим регистры хранения чисел индикации, и скопируем регистр AC_DC_L в Grad (отмечу, что этого можно и не делать-осталось из другой программы, где важно было сохранить первое число)

clrf PeremEd
clrf PeremDes
movf AC_DC_L,0
movwf Grad

Если в регистре Grad сохранен ноль, то можно сразу идти на индикацию, проверку выполняем через 2 бит "Z" регистра Status, при этом регистр Grad перысылаем "самого в себя":

bcf Status,2
movf Grad,1
btfsc Status,2
goto Indik

Далее выполняем декремент регистра Grad и инкремент регистра PeremEd, проверяем результат на равенство 10, делается это вычитанием из регистра PeremEd числа 10, и дальнейшей проверкой флага нулевого результата Z в регистре Status. Обратите внимание регистр   PeremEd при этом не изменяется, так как результат сохраняется в аккумуляторе, если результат равен 0, то переходим по метке Des10, если не равен, то возвращаемся выше по метке vDes и продолжаем отнимать по 1:

decf Grad,1
incf PeremEd,1
bcf Status,2
movlw .10
subwf PeremEd,0
btfsc Status,2
goto Des10
goto vDes

Если вдруг регистр PeremEd стал равен 10, то программа перескочит по метке  Des10, в этом случае обнулится   PeremEd, а к регистру  PeremDes добавится 1, затем также выполняется проверка на переполнение десятков, если вдруг общее значение будет больше 99, то обнуляем регистр  PeremDes, если этого не сделать, то при выполнении вычисляемого перехода программа "выскочит" за пределы нужных значений.

incf PeremDes,1
clrf PeremEd
bcf Status,2
movlw .10
subwf PeremDes,0
btfsc Status,2
clrf PeremDes
goto vDes

На этом сложная часть закончена, если что-то непонятно, то настоятельно рекомендую прочитать текст ещё раз, а затем портенироваться на бумаге, допустим "пишите" в регистр Grad 11 и вычитаете по 1, следите куда пойдёт программа.

В ветку индикации я добавил гашение незначащего нуля:

bcf Status,2
movf Des,1
btfss Status,2
goto CallDes
movlw .10
movwf Des

Проверяем регистр Des на равенство нулю, если равно, то записываем туда 10, и идём дальше, дополнительно 11 строкой в вычисляемом переходе дописываем число b'00000000' (я переписал предыдущую программу под индикатор с общим катодом)

Так же для индикации минусовой температуры я задействовал точку в 2 индикаторе (если есть индикатор с 3 числами, то можно переписать программу для нормального отображения минуса), проверяем нулевой бит регистра minus, если он равен 1, то ставим 0 бит регистра Ed в 1 (для индикатора с общим анодом - ноль)

btfsc minus,0
bsf Ed,0

Выкладываю проект в протеусе и исходник ТУТ. Внимание! если есть индикатор с общим анодом, то меняем местами ПП SemSeg
в конце программы, то что после ; не активно.


Сообщение отредактировал sanyaav - Пятница, 05.04.2013, 23:09


I'll be back
 
aaДата: Четверг, 03.10.2013, 13:18 | Сообщение # 2

Репутация:


Группа:
Новичок


Сообщений: 2
Награды: 0
Статус:Offline


Сообщение отредактировал aa - Четверг, 03.10.2013, 13:46
 
makariusДата: Суббота, 08.03.2014, 04:40 | Сообщение # 3

Репутация:


Группа:
Проверенный паятель


Сообщений: 362
Награды: 12
Статус:Offline
это уже по-лучше, команды Ассемблера, работа с банками...


Каждый выбирает для себя
женщину, религию, дорогу.
Дьяволу служить или пророку -...
 
  • Страница 1 из 1
  • 1
Поиск:

- ЕСТЬ НОВОЕ СООБЩЕНИЕ
- НЕТ НОВЫХ СООБЩЕНИЙ

Copyright Zloy Soft (Company) © 2008 - 2024