Полная версия этой страницы:
вопрос по Си небольшой.
Метценгерштейн
Sep 6 2006, 06:13
У меня организован такой цикл case:
switch(State) {
...
case IDLE: // этот код выполняется если снято с охраны
off(LED);
if( one_wire_main () ) { //если что-то приложили (ключ)
if(USER_KEYS()) {
State = IDLE_HAS_KEY;
}
}
break;
case IDLE_HAS_KEY:
...
Вопрос- вот иду я по проге, приложили USER_KEYS, я сразу прыгаю на метку IDLE_HAS_KEY? Или дохожу до оператора break, потом компилятор анализирует что там было раньше и после break прыгает уже на метку?
Что значит цикл case???
на case IDLE_HAS_KEY: вы попадете только когда оператор switch(State) выполнится заново и при этом State будет = IDLE_HAS_KEY
switch - это всего лишь переключатель в зависимости от State делается JUMP в нужную точку
если хотите в этой же итерации попасть туда используйте goto
например так:
Код
switch(State) {
...
case IDLE: // этот код выполняется если снято с охраны
off(LED);
if( one_wire_main () ) { //если что-то приложили (ключ)
if(USER_KEYS()) {
State = IDLE_HAS_KEY;
goto _IDLE_HAS_KEY:
}
}
break;
case IDLE_HAS_KEY:
_IDLE_HAS_KEY:
...
Harvester
Sep 6 2006, 06:55
Цитата(KRS @ Sep 6 2006, 10:24)

Что значит цикл case???
на case IDLE_HAS_KEY: вы попадете только когда оператор switch(State) выполнится заново и при этом State будет = IDLE_HAS_KEY
switch - это всего лишь переключатель в зависимости от State делается JUMP в нужную точку
если хотите в этой же итерации попасть туда используйте goto
например так:
Код
switch(State) {
...
case IDLE: // этот код выполняется если снято с охраны
off(LED);
if( one_wire_main () ) { //если что-то приложили (ключ)
if(USER_KEYS()) {
State = IDLE_HAS_KEY;
goto _IDLE_HAS_KEY:
}
}
break;
case IDLE_HAS_KEY:
_IDLE_HAS_KEY:
...
Совет правильный, но вообще-то использование команды goto является плохим стилем программирования и очень затрудняет отладку. Лично я построил бы диспетчер следующим образом:
Код
do{
State = IDLE;
off(LED); // этот код выполняется если снято с охраны
...
if( one_wire_main () && USER_KEYS()) //если приложили правильный ключ
State = IDLE_HAS_KEY;
if( ... ) //другое событие
State = ...;
if( ... ) //n-е событие
State = ...;
}while(State == IDLE);
switch(state)
{
case IDLE_HAS_KEY:
...
break;
case ...:
...
break;
default:
...
break;
}
Ну и все это, разумеется, крутится в бесконечном цикле.
Метценгерштейн
Sep 6 2006, 08:29
ясно, с этим вроде разобрался, вот ещё не ясно как лучше сделать.
Уже, наверное, понятно, что я пишу под ключ- таблетку. У меня есть мастер ключ, и есть юзер ключи. Теперь я хочу, чтобы если приложил кратковременно мастер, просто поменяло состояние на другое, а если его удерживаю хотя бы 5 сек, сразу попадаю в др. подпрограмму записи USER ключа. Как это лучше реализовать?
if( MasterKey () ) { //здесь- что, если приложен мастер
//здесь опишем процесс, записи новых ключей, только из IDLE
//если прижали мастер на 5 сек
CntT0= 0;
while (CntT0 !=95) //если в теч. 5 сек. приложили мастер, вызываю запись USERa
...
WRITE_KEY();
...
State = IDLE_HAS_KEY;
...
State = IDLE_HAS_KEY; это состояние, куда я должен попать если кратковременно приложил- отпучтил ключ, а если 5 сек. удерживаю, то в ф-ю WRITE_KEY();
как бы это завернуть покрасивше?
Метценгерштейн
Sep 6 2006, 09:22
так корректно будет?
CntT0= 0;
if( !one_wire_main () ) { //если отпустили ключ
if (CntT0 !=95) //если не досчитал до 5 сек., то меняю сост. на IDLE_HAS_KEY
State = IDLE_HAS_KEY;
else
WRITE_KEY();
}
Harvester
Sep 6 2006, 11:41
Цитата(Метценгерштейн @ Sep 6 2006, 12:29)

ясно, с этим вроде разобрался, вот ещё не ясно как лучше сделать.
Уже, наверное, понятно, что я пишу под ключ- таблетку. У меня есть мастер ключ, и есть юзер ключи. Теперь я хочу, чтобы если приложил кратковременно мастер, просто поменяло состояние на другое, а если его удерживаю хотя бы 5 сек, сразу попадаю в др. подпрограмму записи USER ключа. Как это лучше реализовать?
...
Кстати, а как Вы собираетесь определять, что в течение всех этих 5 секунд был приложен мастер ключ? Постоянно считывать и проверять его код? - неудобно.
В многих подобных системах используется след. алгоритм:
1. При кратковременном замыкании контактов считывателя переходим в режим ожидания мастер-ключа.
2. Если в течение заданного интервала обнаружили мастер - переходим в режим записи USER-ключа, иначе - возвращаемся в режим ожидания.
3. Ну, и если в режиме программирования в течение разумного промежутка не был обнаружен новый ключ или же при повтороном замыкании контактов считывателя - также возвращаемся в дежурный режим.
Метценгерштейн
Sep 8 2006, 07:35
немного конкретизирую задачу- при приложении мастер- ключа, устойчиво переходит из состояний охрана- снято. Но, я хочу, чтобы если подержал подольше мастер- ключ- секунды 4, то попадаю в ф-ю записи юзер- ключа. Для начала, надо, чтобы я устойчиво попадал в эту прогу, а там мигал хитро светодиодом. Подскажите, плз. Вот образец куска проги общей.
if( MasterKey () ) { //здесь- что, если приложен мастер
//здесь опишем процесс, записи новых ключей, только из IDLE
//если прижали мастер на 5 сек
//**************************** ***************** *******************************
/* CntT0= 0;
if( !one_wire_main () ) { //если отпустили ключ
if (CntT0 < 95) //если не досчитал до 5 сек., то меняю сост. на IDLE_HAS_KEY
State = IDLE_HAS_KEY;
else
WRITE_KEY();
} */
//************************************* **************** ************************
State = IDLE_HAS_KEY;
}
}
здесь закомментировано, так оно просто переключается между состояниями, ф-я записи ключей WRITE_KEY();
попав в неё, я хочу помигать лампочкой.
Как лучше тут описать? Чтобы я держал ключ, как только время достигло 4 сек, оно прыгал в ту ф-ю, а не ждало пока я ключ отпущу, и потом только прыгало?
Harvester
Sep 8 2006, 12:41
Цитата(Метценгерштейн @ Sep 8 2006, 11:35)

...
Как лучше тут описать? Чтобы я держал ключ, как только время достигло 4 сек, оно прыгал в ту ф-ю, а не ждало пока я ключ отпущу, и потом только прыгало?
По логике нужно использовать 1-й вариант - а то как пользователь узнает, что мастер-ключ уже можно отпускать.
Метценгерштейн
Sep 11 2006, 10:23
while ( MasterKey () ) { //пока ещё приложен ключ
CntT0= 0;
if (CntT0 == 95) { //если не досчитал до 5 сек., то меняю сост. на IDLE_HAS_KEY
WRITE_KEY ();
}
}
такая конструкция была бы правильной? В ф-ии WRITE_KEY (); я просто мигаю лампочкой.
Но вот что- то оно не мигает....
Цитата(Метценгерштейн @ Sep 11 2006, 14:23)

while ( MasterKey () ) { //пока ещё приложен ключ
CntT0= 0;
if (CntT0 == 95) { //если не досчитал до 5 сек., то меняю сост. на IDLE_HAS_KEY
WRITE_KEY ();
}
}
такая конструкция была бы правильной? В ф-ии WRITE_KEY (); я просто мигаю лампочкой.
Но вот что- то оно не мигает....

Не так это делается
После проверки ключа на валидность надо секунды на три блокировать ввод.
Иначе можно нарваться на двойное переключение состояния, люди по разному
ключ прикладывают.
После ввода ключа, блокируешь ввод,
очищаешь счетчик в таймерном прерывании,
отсчитываешь 3 - 4 секунды
и разрешаешь ввод ключа.
Если ключ все еще приложен, он тут же считается и вот тут то и надо принимать решение
о переходе в режим ввода новых ключей.
Успехов
Shurmas
Sep 11 2006, 13:41
Пример с сетью 1-wire с "таблеткой" и другими DSxxxx есть в симуляторе PROTEUS - вы можете отладить код на ПК.
Пример находится в папке:
SAMPLES\One-Wire\NETWORK
Метценгерштейн
Sep 14 2006, 11:17
ещё вопрос по Си (простой)
в таком виде
case ARMED: // этот код выполняется под охраной
if(signal(SENS)) {
on(SIRENA);
CntT0= 0;
if (CntT0 == 700) { // продолжительность воя сирены - подобрать
off(SIRENA);
}
}
это фрагмент, попал я в режим под охраной, как только поступил сигнал с датчика, включаю сирену, теперь надо, чтобы она либо сама отключилась через время, либо я ключом её выключил. Считает счетчик, если равно количесву прерываний счетчика 700, сирена выключается. Не понятно по скобкам, этот таймер у меня внутри ф-ии, я туда всегда буду попадать по прерыванию счетчика? или я там буду, пока SENS (датчик) сработал? Вот здесь непонятность небольшая
dezzer
Sep 14 2006, 11:44
Цитата
Не понятно по скобкам, этот таймер у меня внутри ф-ии, я туда всегда буду попадать по прерыванию счетчика?
Таймера внутри функции лично я не вижу. По прерыванию от него вы будете попадать в соответствующую ISR (программу обслуживания прерываний). А вот, что вы будуте в этой ISR делать (просто отключать сирену, флаг какой-то устанавливать, что пора бы её выключить) - это уже ваше дело.
Метценгерштейн
Sep 14 2006, 12:02
всё правильно, я немного не так выразился, по прерыванию от таймера я попадаю сюда
#pragma vector = TIMER0_OVF0_vect
__interrupt void overflow_timer1(void)
{
CntT0++;
}
Здесь увеличиваю переменную
А что у меня получилось? Сначала сработал датчик, я попал в эту ф-ю, включил сирену, обнулил
CntT0=0;
теперь надо, чтобы при проверке при количеству подсчетов =700, выполнялось отключить сирену. Уже вижу, что запутался. Я хотел вот что спросить, здесь
if(signal(SENS)) {
я буду считать до 700 если это условие постоянно истина? Т.е. все время идет сигнал от датчика? как только нет, я вываливаюсь из цикла и уже не проверяю на 700?
Цитата(Метценгерштейн @ Sep 14 2006, 15:02)

всё правильно, я немного не так выразился, по прерыванию от таймера я попадаю сюда
#pragma vector = TIMER0_OVF0_vect
__interrupt void overflow_timer1(void)
{
CntT0++;
}
Здесь увеличиваю переменную
А что у меня получилось? Сначала сработал датчик, я попал в эту ф-ю, включил сирену, обнулил
CntT0=0;
теперь надо, чтобы при проверке при количеству подсчетов =700, выполнялось отключить сирену. Уже вижу, что запутался. Я хотел вот что спросить, здесь
if(signal(SENS)) {
я буду считать до 700 если это условие постоянно истина? Т.е. все время идет сигнал от датчика? как только нет, я вываливаюсь из цикла и уже не проверяю на 700?
Может невпопад, за веткой не слежу. Я бы сделал так:
Код
#pragma vector = TIMER0_OVF0_vect
__interrupt void overflow_timer1(void)
{
static CntT0 = 0;
if (State == ST_DELAY)
{
if (++CntT0 >= DELAY_5S)
{
CntT0 = 0;
State = ST_WRITE;
}
}
}
2
Метценгерштейнимхо, было бы неплохо нарисовать граф состояний устройства с условиями переходов, а то, похоже, Вы уже действительно запутались.
Большинство моих программ для контроллеров вписываются в примерно такую структуру:
Код
void main(void) {
// startup - разбор причины сброса, соответствующая инициализация
...
for(;;) {
// синхронные события
if(Flags.b._10ms) {
Flags.b._10ms = 0;
GetSlowInputs(); // опрос и фильтрация медленных входов и датчиков
if(DoorLockTmr) {
if(!--DoorLockTmr) {
ODoorLock = 0;
ODoorUnlock = 0;
}
}
...
}
if(Flags.b._500ms) {
Flags.b._500ms = 0;
if(MKeyBtn0HoldTmr) {
if!--MKeyBtn0HoldTmr) {
Mode.b.Prog = 1;
ProgModeTmr = 30 sec;
}
};
if(SirenOnTmr) {
if(!--SirenOnTmr) OSiren = 0;
};
if(LightOnTmr) {
LightOnTmr--
if(!LightOnTmr) {
OLight = 0;
LightPtrn = 0;
}
else {
if(LightPtrn & 1) OLight = 1;
else OLight = 0;
сsr(LightPtrn) // циклический сдвиг вправо
}
}
else if(LightPtrn) {
if(LightPtrn & 1) OLight = 1;
else OLight = 0;
lsr(LightPtrn) // логический сдвиг вправо
}
...
}
// обработка изменений по медленным входам из GetSlowInputs()
// - вынесена из if(Flags.b._10ms){} для удобства восприятия
if(PortAChg.b.ShockSens) {
if(PortA.b.ShockSens) { // сработал датчик удара
if(Mode.b.Armed && SensSts.b.SensEn) {
SirenOnTmr = 15 sec;
LightOnTmr = 25 sec;
LightPtrn = 0xaa;
...
}
}
}
// асинхронные события:
// быстрые входы обрабатываются или своими прерываниями или в быстрых
// таймерных прерываниях с выставлением флагов событий;
if(RcvSts.b.ValidPktRcvd) {
if(RcvSts.b.BtnPressed) {
if(RcvSts.b.MasterKey) {
if(BtnPrsd.b._0) {
MKeyBtn0HoldTmr = 5 sec;
...
}
if(BtnPrsd.b._1) ...
...
}
else {
if(BtnPrsd.b._0) {
if(Mode.b.Prog) {
// сформировать очередь для записи в EEPROM кода брелока
}
else {
if(Mode.b.Armed) {
Mode.i = 1<<MDisarmed;
DoorLockTmr = DoorUnlockTime;
ODoorUnlock = 1;
LightPtrn = 0x05;
}
else if(Mode.b.Disarmed) {
Mode.i = 1<<MArmed;
DoorLockTmr = DoorLockTime;
ODoorLock = 1;
LightPtrn = 0x01;
}
...
}
}
if(BtnPrsd.b._1) ...
...
}
...
}
if(RcvSts.b.BtnReleased) {
if(RcvSts.b.MasterKey) {
if(BtnRlsd.b._0) MKeyBtn0HoldTmr = 0;
...
}
else {
if(BtnRlsd.b._0) ...
...
}
}
RcvSts.b.ValidPktRcvd = 0;
}
// обработка очереди записи в EEPROM
if(!EEQueue->EESts.b.Busy) {
...
}
...
} // end for(;;)
} // end main()
// в синтаксисе PICC и для PIC'ов - мне пока так проще:)
interrupt void isr(void) {
do {
if(TMR0IF) {
TMR0 += по потребностям;
TMR0IF = 0;
// Flags.b._100us = 1;
здесь обрабатываем быстрые входы, н-р, приемник
желательно, на ассемблере:)
if(!RcvSts.b.ValidPktRcvd) {
...
}
};
if(TMR1IF) {
TMR1ON = 0;
TMR1 += по потребностям;
TMR1ON = 1;
TMR0IF = 0;
Flags.b._10ms = 1; // 10 ms только для примера
};
if(...IF) {
...
Flags.b.xxx = 1;
};
...
} while(any (IF & IE)); // do{}while, естесно, по вкусу/необходимости
}
Компилировать это, естесно, не стоит.
hints:
Используйте count-down программные таймеры - проще обрабатывать, код короче, можно использовать как признак состояния.
Не злоупотребляйте switch'ами - код для них что от PICC (для PIC'ов, естесно), что от WinAVR и IAR трудно назвать оптимальным. Но если места и времени (контроллера

не жалко, то вперед.
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.