roboforum.ru

Технический форум по робототехнике.

Фоновый режим

Re: Фоновый режим

dccharacter » 05 апр 2011, 23:58

tallarna писал(а):для начала - у atmega8 часов реального времени (хранящего часы/минуты/секунды) нету

но можно сделать rtc без хардового rtc

Re: Фоновый режим

GrayHunter » 06 апр 2011, 00:57

Внёс некоторые изменения в алгоритм:
- протечка №1 теперь останавливает всё, а протечка №2 блокирует лишь модуль №1.
- светодиод показывает наличие/отсутствие протечек.
- модуль №1 и №2 - это увлажнители. Модуль №3 - это растения/полив.
- "П_модуль" - это тумблер принуждения, так сказать :)
- исправил недочёт сдвига 24 часов на время_полива каждые 24 часа, теперь полив/модуль №3 будет точно каждые 24 часа.
В каждом увлажнителе стоит два датчика - верхний и нижний, чтобы узнать уровень воды соответственно у края и у дна.

Получилось вот что:
// расстановка "флагов"
1. Если есть протечка 1 - то подпрограмма "Авария"
2. Если модуль №1 вЫключен - то сделать module_1_full = 1 и перейти к 6
3. Если П_модуль №1 включен - то сделать module_1_full = 0 и перейти к 6
4. Если и нижний и верхний датчик модуля 1 = 0 - то сделать module_1_full = 0
5. Если и нижний и верхний датчик модуля 1 = 1 - то сделать module_1_full = 1
6. Если есть протечка 2 - то сделать module_1_full = 1 и зажечь светодиод, иначе погасить светодиод
7. Если модуль №2 вЫключен - то сделать module_2_full = 1 и перейти к 11
8. Если П_модуль №2 включен - то сделать module_2_full = 0 и перейти к 11
9. Если и нижний и верхний датчик модуля 2 = 0 - то сделать module_1_full = 0
10. Если и нижний и верхний датчик модуля 2 = 1 - то сделать module_1_full = 1
11. Если модуль №3 вЫключен - то сделать module_3_full = 1 и перейти к 16
12. Если П_модуль №3 включен - то сделать module_3_full = 0 и перейти к 16
13. Если module_3_in_progress = 1 - то перейти к 15
14. Если время_с_последнего_полива > 24 часов - то сделать module_3_full = 0 и сбросить время_с_последнего_полива и начать считать время_полива и сделать module_3_in_progress = 1, иначе перейти к 16
15. Если время_полива > n секунд - то сделать module_3_full = 1 и прекратить считать время_полива и сбросить время_полива и сделать module_3_in_progress = 0
16. УПРАВЛЕНИЕ: Если module_1_full = 0 - то открыть клапан №1 и включить помпу, иначе закрыть клапан №1
17. Если module_2_full = 0 - то открыть клапан №2 и включить помпу, иначе закрыть клапан №2
18. Если module_3_full = 0 - то открыть клапан №3 и включить помпу, иначе закрыть клапан №3
19. Если module_1_full = 0 и module_2_full = 0 и module_3_full = 0 - то выключить помпу
20. Перейти к 1

:::::::Подпрограмма:::::::
Авария (протечка №1):
1) выключить помпу и закрыть все клапаны
2) зажечь светодиод
3) Если протечка №1 - перейти к 3)
4) погасить светодиод и выйти из подпрограммы


ВОПРОС: помогите с пунктами 14 и 15 пожалуйста.
(это наверняка поможет вам, опытным, а мне таймеры на Ассемблере- тёмный лес)

Re: Фоновый режим

boez » 06 апр 2011, 13:01

Не, ты уж разберись сам, заодно опыт приобретешь :)

Ничего мистического в таймерах нету. Ща объясню. Предыдущий пример был для толстого таймера, типа 16-битного таймера 1 меги8, который можно настроить на срабатывание раз в секунду. Но меня одолела жадность и я буду использовать таймер 0.

Смотри, вот есть в даташите страница 70 с описанием регистров таймера 0. Начиная с нее надо читать.
А собственно нужных регистров там всего два - TCCR0 и TCNT0. Это самый простой таймер в меге8, он вообще нифига не умеет, токо считать.
Считает он до 256, если хочется чтобы он считал до другого числа - надо начинать не с нуля.

Пускай у нас кварц 8 МГц. У таймера есть предделитель (prescaler), умеет делить на разные числа, на какие - см. даташит, стр. 70. Нам подойдет число 256 (из 1024 уже не получаются целые секунды). 8000000 / 256 = 31250. Это значит, что 31250 раз в секунду счетчик таймера будет увеличиваться на 1. Но 31250 = 250*125. Запрограммируем таймер так, чтобы его период был 250 тиков, тогда он будет переполняться 125 раз в секунду.

Чтобы таймер переполнялся каждые 250 тиков - ему надо после переполнения вписать число 256 - 250 = 6. Тогда в TCNT0 будут такие числа: 6,7,8,...,254,255,6,7,8...

Получаем такую программу:

Код: Выделить всёРазвернуть
Инициализация:
записать число 4 в TCCR0. Почему 4 - см. стр. 70 даташита.
установить бит 0 в TIMSK (это бит TOIE0, разрешение прерываний переполнения таймера 0)


Обработчик прерывания Timer0 overflow:
записать 6 в TCNT0

прочитать глобальную переменную SubSecondCount
прибавить 1
записать полученное значение в SubSecondCount
если получили >=125:
   обнулить SubSecondCount
   прочитать Time
   +1
   записать Time


Вот как-то так. Time увеличивается на 1 раз в секунду.

SubSecondCount - однобайтовая переменная, с ней никаких проблем, а вот Time- минимум 3 байта, чтобы сутки (86400) поместились. В С мне с этим проще - объявил переменную long и компилятор сам всю арифметику сгенерит. А ты уж как-то ручками, младший байт add, остальные adc, или как там оно делается чтобы переносы учитывались. И еще, в основном коде когда будешь эту 3-4-байтовую фигню читать в регистры для сравнения или копирования - запрети на это время прерывания, иначе возможны глюки (ты прочел 1 байт, счетчик тикнул и следующие байты ты прочел от нового значения).

Ну а в основной программе вообще делается ровно то что я написал, копирование и сравнение.

Re: Фоновый режим

GrayHunter » 09 апр 2011, 16:43

Долго сидел, читал, в итоге, Прошу проверить и ответить на вопросы внизу
(здесь только код, касающийся таймера, а то вся программа уже на 200 строк - не хочу вас утомлять) (ДатаШит AtMega8):

Код: Выделить всёРазвернуть
.def     Temp=R16  ; разное
.def     Temp_2=R17  ; разное 2
.def     Time=R18  ; показания таймера в Секундах

.cseg
.org 0


;************_ НАСТРОЙКА ПРЕРЫВАНИЙ _************
rjmp Reset
rjmp INT_0
rjmp INT_1
rjmp TIMER2_COMP
rjmp TIMER2_OVF
rjmp TIMER1_CAPT
rjmp TIMER1_COMPA
rjmp TIMER1_COMPB
rjmp TIMER1_OVF
rjmp TIMER0_OVF
rjmp SPI_STC
rjmp USART_RXC
rjmp USART_UDRE
rjmp USART_TXC
rjmp ADC
rjmp EE_RDY
rjmp ANA_COMP
rjmp TWI
rjmp SPM_RDY

;Reset:
INT_0:
INT_1:
TIMER2_COMP:
TIMER2_OVF:
TIMER1_CAPT:
TIMER1_COMPA:
TIMER1_COMPB:
TIMER1_OVF:
;TIMER0_OVF:
SPI_STC:
USART_RXC:
USART_UDRE:
USART_TXC:
ADC:
EE_RDY:
ANA_COMP:
TWI:
SPM_RDY:
         reti
         
         
   Reset:    ldi Temp,0b11111111   ;настройка портов
             out DDRB,Temp
   
             ldi Temp,4
             out TCCR0,Temp
   
             ldi Temp,0b00000001   ;разрешение прерываний переполнения таймера 0
             out TIMSK,Temp
            
             ldi Temp, low(RAMEND)  ; инициализация стека
           out SPL, Temp  ; инициализация стека
           ldi Temp, high(RAMEND)  ; инициализация стека
           out SPH, Temp  ; инициализация стека
          
           sei ;разрешить прерывания
         

           Begin: //// ЗДЕСЬ БУДЕТ САМА ПРОГРАММА ////
           rjmp Begin
         
         
;************_ ОБРАБОТЧИК ПРЕРЫВАНИЯ Timer0 overflow _************         
   TIMER0_OVF: ldi Temp,6
            out TCNT0,Temp
            ldi Temp,0
              mov Temp,SubSecondCount ; прочитать глобальную переменную SubSecondCount в Temp
                ldi Temp_2,1
                add Temp,Temp_2 ; прибавить 1 к Temp
                out SubSecondCount,Temp ; записать полученное значение в SubSecondCount
                CPI SubSecondCount, 125
                BRSH Plus_Second ; если получили >=125
   TIMER0_OVF_Exit: reti ; выход из обработчика
   Plus_Second: CLR SubSecondCount
             ldi Temp,0
             mov Temp,Time ; прочитать Time в Temp
             add Temp,Temp_2 ; прибавить 1 к Time
             out Time,Temp ; записать полученное значение в Time
             rjmp TIMER0_OVF_Exit ; к выходу из обработчика


ВОПРОСЫ:
1) правильно ли я всё написал? Если что-то не так - Пожалуйста, помогите исправить, т.к. больше информации у меня нет (основной источник - радиокот).
2) глобальная переменная SubSecondCount просто висит в воздухе? ничего не нашёл про неё. Подскажите плз, что с ней делать и как именно (код)?
3) "Reset:" меня как-то смущает, не будет ли у меня программа по окончанию перепрыгивать к "Reset:", а не к "Begin:"?

Re: Фоновый режим

boez » 09 апр 2011, 17:15

Не, ну идея понята правильно, но ошибок хватает.
Во-первых, SubSecondCount таки надо объявить - или как регистр, или в ОЗУ. И в соответствии с этим писать команды работы с ней - или mov, или sts/lds. А то как-то странно, тут играем, тут не играем, тут mov, там out...
Во-вторых, Time должна быть минимум 3 байта, а не один. Ну и прибавление единицы будет выглядеть как-то так (Time - 3 байта в ОЗУ):
Код: Выделить всёРазвернуть
lds Temp1, Time
lds Temp2, Time+1
lds Temp3, Time+2
ldi Temp4, 1
add Temp1, Temp4
clr Temp4
adc Temp2, Temp4
adc Temp3, Temp4
sts Time, Temp1
sts Time+1, Temp2
sts Time+2, Temp3

Тут Temp1-Temp4 - какие-нибудь подходящие регистры.

А вообще не мучай себе моск, выучи С, потом будет намного проще. Вот эта жопа, на которую я потратил минут 10 пока понаходил в таблице команд АВР подходящие, на С выглядит как Time=Time+1. Пофиг какого размера Time. Пусть компилятор думает, какие временные регистры использовать и как все это куда рассовать.

Re: Фоновый режим

GrayHunter » 09 апр 2011, 18:58

Работа над ошибками (проверьте правильно ли теперь, пожалуйста):

1) теперь так:
Код: Выделить всёРазвернуть
.def     Temp=R16  ; разное
[b].def     SubSecondCount=R17
.def     Temp1=R18
.def     Temp2=R19
.def     Temp3=R20
.def     Temp4=R21

   .dseg
   Time: .byte 3[/b]

.cseg
.org 0


2) Обработчик прерывания теперь так:
Код: Выделить всёРазвернуть
;************_ ОБРАБОТЧИК ПРЕРЫВАНИЯ Timer0 overflow _************          
   TIMER0_OVF: ldi Temp,6
            out TCNT0,Temp
            [b]ldi Temp,1
              add SubSecondCount,Temp ; прибавить 1 к SubSecondCount[/b]
                CPI SubSecondCount, 125
                BRSH Plus_Second ; если получили >=125
   TIMER0_OVF_Exit: reti ; выход из обработчика
   Plus_Second: [b]ldi SubSecondCount,0
lds Temp1, Time
lds Temp2, Time+1
lds Temp3, Time+2
ldi Temp4, 1
add Temp1, Temp4
clr Temp4
adc Temp2, Temp4
adc Temp3, Temp4
sts Time, Temp1
sts Time+1, Temp2
sts Time+2, Temp3[/b]
             rjmp TIMER0_OVF_Exit ; к выходу из обработчика

Правильно?
Если нет - подскажите как правильно, пожалуйста.

3) И ещё, Вы писали
9. Если (время - последнее время полива) > 24 часов - поставить флаг полива, засечь время начала полива
10. Если флаг полива и (время - время начала полива) > n секунд - снять флаг полива, обновить время последнего полива
Подскажите, пожалуйста, как именно писать подчёркнутое, а то я пока плохо представляю, как это работать с числом, разбитым на 3 части... :oops:
например, я не понял, зачем мы вот это делали/что это значит -
Код: Выделить всёРазвернуть
adc Temp2, Temp4
adc Temp3, Temp4
просто прибавили ноль? А почему нельзя это просто не писать, ведь при прибавлении нуля к числу число не меняется...
В общем, с таким 3-х битным числом я бы хотел вас попросить подсказать мне как:
- его сравнивать (например, чтобы понять меньше ли оно 24 часов или уже нет)
- как его обнулять (например, после достижения 24 часов, т.е. чтобы новые сутки отсчитывать стал)

Re: Фоновый режим

boez » 11 апр 2011, 11:25

GrayHunter писал(а):просто прибавили ноль?

Просто прибавили - это add. А мы сделали adc. Сорри, щас нету времени много писать, вот примеры длинной арифметики на AVR. почитай что такое перенос в следующий байт.
http://controllersystems.com/books/prak ... racii.html
http://microelectronic.at.ua/publ/umnoz ... r/1-1-0-17

Re: Фоновый режим

GrayHunter » 11 апр 2011, 11:55

Хорошо, про арифметику (3-ий вопрос) прочитаю. Спасибо.

А что насчёт вопросов 1 и 2? Всё правильно?

Re: Фоновый режим

boez » 11 апр 2011, 18:34

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

Re: Фоновый режим

GrayHunter » 11 апр 2011, 21:03

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

Re: Фоновый режим

boez » 12 апр 2011, 10:49

А там не надо обнулять, там надо копировать переменную Time в соответствующие переменные
Код: Выделить всёРазвернуть
LastWaterTime .byte 3
StartWaterTime .byte 3

Копировать просто побайтово, младший в младший, средний в средний, старший в старший. На время чтения переменной Time обязательно запрещать прерывания. А сравнивать примерно так же, как складывать: cp младшие байты, cpc средние, cpc старшие, по биту переноса определяем было ли второе значение больше первого. Вычитать тоже так же, sub, sbc, sbc.

а на сях проще ;)


cron
Rambler\'s Top100 Mail.ru counter