|
|
|
|
Сергей К: У меня размер кода уже вырос до 50% от общего объема памяти, но на нехватку памяти в сегменте перетал ругаться. Дык в чем и отличие инлайн функций , что компилятор каждый раз впихивает ее тело заместо вызова - экономим стек -растет прога ...
|
|
|
Интереснейшая тема совсем заглохла.... Решусь оживить вопросами. Есть следующий алгоритм(опишу для лучшего понимания): Две кнопки должны управлять периодом ШИМ, при кратковременном нажатии делать фиксированный шаг (в чем угодно в процентах или по счетчику), а при длительном нажатии менять непрерывно с большим шагом. В тоже время АЦП делает 10 выборок и выводит на 4 значный 7 сегментный индикатор. Использовать задержки или прерывания для обработки нажатий и подавления дребезга отпадает, использую таймер1. Для времени выборок ацп таймер0, но таймер1 зависит от таймера0 (точнее от его делителя), всегда так или нет? Можно ли убрать эту зависимость ? И последнее как правильно или грамотнее организовать управление кнопками? Вот собственно код: #include <16F877.h> #device adc=10 #FUSES NOWDT //No Watch Dog Timer #FUSES HS //High speed Osc (> 4mhz) #FUSES NOCPD //No EE protection #FUSES NOWRT //Program memory not write protected #use delay(clock=10000000) #define BASE 2 #define DIG_BASE 10 // основание системы исчисления для перевода #define MAX_SIZE 4 // количество 7 сегментных индикаторов /* выходной массив символов (экранная область) */ unsigned CHAR SYMBOLS[DIG_BASE] = {63, 6, 91, 79, 102, 109, 125, 7, 127, 111}; unsigned CHAR out[MAX_SIZE]; static CHAR cADCflag; long INT num,x; float d; INT pwmDuty = 100; // начальное значение ШИМ INT i,m,z; INT speed = 1; // Начальная скорость изменения ШИМ int16 t,t1; char cCnt,num7seg; // переменная считований ацп и номер сегмента long cADCtotal, cADCval; // переменные значений ацп /* ПРЕРЫВАНИЕ */ #INT_RTCC VOID TIMER_INTERRUPT (void) // функция прерывания от счетчика 0 { set_rtcc (100); // перезапустить счетчик cADCflag = 1; // установить флаг для чтения ацп в главном цикле программы } /* ОСНОВНОЙ ЦИКЛ ПРОГРАММЫ */ void main() { setup_adc_ports(RA0_RA1_RA3_ANALOG); setup_adc(ADC_CLOCK_DIV_32); /* установка таймера0 так чтобы ацп читалось каждые 10mS */ setup_timer_0(RTCC_INTERNAL|RTCC_DIV_64); setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); setup_timer_2(T2_DIV_BY_4, 127, 1); setup_counters(RTCC_INTERNAL,RTCC_DIV_64); //надо ли? или можно прерывание делать от таймера0? setup_ccp1(CCP_PWM); set_tris_e (0xff); // in set_tris_d (0x00); // out set_tris_b (0x00); // out set_adc_channel (0); enable_interrupts(int_RTCC); enable_interrupts(global); set_rtcc ( 100 ); // старт счетчика начиная со 100 cADCtotal = 0; cCnt = 0; // начальная установка счетчика считованиц ацп WHILE (1) { if ( cADCflag == 1 ) // если прерывание установило флаг то { cADCflag = 0; // убрать флаг cADCval = read_adc(); // прочитать ADC cADCtotal += cADCval; // добавить значение к общему, для дальнейшего усреднения if ( cCnt++ == 9 ) // счетчик количества считований ацп если прочитано 10 раз то { disable_interrupts (int_RTCC); // запретить прерывание на время вывода на семисегментный индикатор cCnt = 0; // перезапустить счетчик cADCtotal /= 10; // поделить на 10 для вычисления среднего значения d = ( cADCtotal * 30 ) / 10.24 ; // мат. операции для получения нужного выв. числа i = 3; NUM = d ; for(num7seg=8;1;1) // цикл вывода на 7 индикатор справа на лево (1,2,4,8) { delay_ms (10); out[i] = 0; // выводимая цыфра равна 0 m = NUM % 10; // находим остаток от деления числа на основание 10 out[i] = SYMBOLS[m]; // этот остаток есть ВЫВОДИМАЯ ЦИФРА NUM /= DIG_BASE; // уменьшаем число в DIG_BASE раз output_b ((num7seg)); if (num7seg==2){out[i]=out[i]+128;} // если выводимая цифра третья то добавим точку output_d ( ~ (out[i])); // выводи инвертировав в порт D i = i - 1; // уменьшаем позицию выведения числа num7seg = num7seg / 2 ; // переключение вывода на следующюю позицию if(num7seg<1){ num7seg = 8; break; } // Если вывели все цифры то выход из цикла } cADCtotal = 0; // обнулить полученое от ацп среднее значение enable_interrupts (int_RTCC); // разрешыть прерывание } } /* Обработка кнопок */ t = get_timer1(); // получить значение таймера 1 if(t>30000){t1++ ;} // если больше 30000 то переменная t1 увеличется на 1 if(input(PIN_E0)) // если нажата кнопка 1 то { if(t1==6){speed = 2; x = (x-speed);} //если t1 равно 6 то уменьшаем период ШИМ на 2 if(t1>1200){speed = 10; x =(x-speed); t1 = 1190;} //если t1 равно 1200 то уменьшаем период ШИМ на 10 и t1 = 1190 } else if (input(PIN_E1)) //иначе если нажата кнопка 2 то { if(t1==6){speed = 2; x=(x+speed);} // увеличиваем... if(t1>1200){speed = 10; x=(x+speed); t1 = 1190;} // .... } else {speed = 1; t1 = 1; } //иначе сбрасываем обе переменные /* проверка допустимых значений ШИМ, если не делать жутко парится протеус */ if(x<10){x=10;} if(x>250){x = 250;} set_pwm1_duty(x); // задаем период ШИМ после всех проверок } } |
|
|
Или файл проекта, а то код как тут удобней вставлять незнаю можно ли. CCS-PICC 4.057 173462.rar |
|
|
Может это в корне не верно, но я делал опрос кнопок и антидребезг вообще без таймеров. Опрос кнопок происходил при каждом проходе программы и после первого раза ставился флаг, мол в прошлый раз кнопка была нажата. Если в следующем проходе кнопка снова нажата, то происходит действие. Если программа короткая, то можно сделать типа как счетчик проходов, но я так не делал, хватало одного флага. |
|
|
Я бы тоже отказался от таймеров, так как когда они все включены (может и не поэтому не знаю отладить в МПЛАБ не могу слабо разбираюсь во всяких там регистрах, ячейках памяти, китайская грамота для меня пока что) то появляются то исчезают странные глюки с пляской ШИМ, при симуляции в протеусе, как будто пропускает один период ШИМ. А как без таймеров можно организовать вот так: Одно короткое нажатие сделали 1 шаг не больше (при длительности нажатия до 1сек.), а при удержании кнопки более 1с делаем большие шаги каждый цикл программы или с заданной дискретностью? На счет выставления флагов конечно подумаю спасибо за идею, сам как то и не подумал что можно считать сколько раз флаг выставлялся и от сюда плясать. Программу буду дописывать еще много чего впихнуть думаю, да и код перенесу на 18F4685, поэтому пока сомневаюсь в своем сочинительстве . |
|
|
dosikus: Vladikas: А я угробил мплаб и си компилятор... Фиг знает что случилось, но ничего не работает. Дурацкий линукс........ Ты часом не в wine ставил? Не заметил сразу. Да, это было в вайне, но уже мне это не нужно. Left Radio: А как без таймеров можно организовать вот так: Одно короткое нажатие сделали 1 шаг не больше (при длительности нажатия до 1сек.), а при удержании кнопки более 1с делаем большие шаги каждый цикл программы или с заданной дискретностью? Если нажать кнопку и отпустить, то "плюс один". Если держать кнопку, то после каждого прохода программы будет в "счётчик проходов" добавляться "плюс один", когда счётчик достигнет определённого значения, то делать не "плюс один" для нашего "изменяемого параметра", а "плюс десять", например. Но думаю, что прога будет бегать очень шустро, поэтому нужно будет два счётчика проходов сделать или сравнивать содержимое одного счётчика проходов с двумя значениями. Первое - малое, для шагов изменения параметра в одну единицу и с большим, для скачков в 10 единиц. Надеюсь понятно... По-моему так никто не делает ------------- Про шустрость проги... Поменять значение параметра при проходе, когда поднят флаг "кнопка была нажата при прошлом проходе", а при следующем проходе значение не менять, потому что поднят флаг "значение менялось в прошлый раз". Вот как-то так... зы Давно я не программировал, интерес потерял для себя что-то делать, так что надеюсь мои алгоритмы не очень бредовые |
|
|
Делать антидребезг, не залезая в прерывания по таймеру - это значит тормозить выполнение основной программы, т. к. в любом случае вам будут нужны задержки между опросами кнопок. Конечно, если программа простая и имеет один большой цикл, то можно в этом цикле организовать счетчик времени антидребезга. А если циклов несколько, что бывает гораздо чаще? Да еще и вхождение в цикл зависит от условия? Что тогда? Конечно, наверное в принципе можно "встать на уши" и запихнуть антидребезг и в многопетлевую основную программу. Но это запихивание не будет универсальным. Каждый раз вам придется проявлять чудеса изобретательности. Есть ли в этом смысл? Таймеры напрягать жалко? Думаете, лучше напрячь себя? И еще. В прерывании по таймеру можно не только кнопки обрабатывать, а еще делать кучу всякой всячины. Например организовывать задежки любых длительностей без ущерба основной программе и т. д. |
|
|
Left Radio: Для времени выборок ацп таймер0, но таймер1 зависит от таймера0 (точнее от его делителя), всегда так или нет? это как? Они независимые. У кажного свои делители. Делитель от TMR0 может подключатся к WDT. Left Radio: setup_counters(RTCC_INTERNAL,RTCC_DIV_64); //надо ли? или можно прерывание делать от таймера0? что это? У меня все без такого работает. Модуль ШИМ использует TMR2 (DS30292C стр. 57) |
|
|
Сергей К ◊: "Они независимые. У кажного свои делители. Делитель от TMR0 может подключатся к WDT." Понял, спасибо. Сергей К ◊: "что это? У меня все без такого работает." Ну вроде счетчик, в примерах надыбал. Да без него все работает. Zandy : " Делать антидребезг, не залезая в прерывания по таймеру - это значит тормозить выполнение основной программы.... И еще. В прерывании по таймеру можно не только кнопки обрабатывать, а еще делать кучу всякой всячины. Например организовывать задежки любых длительностей без ущерба основной программе и т. д. " Вот и хотел узнать как знающие люди делают, если можно приведите код анти-дребезга с применением прерываний или ссылкой на примеры. А разве например команда выполняющаяся в прерывании delay_ms(100) не затормозит основную программу? |
|
|
Vladikas : "Надеюсь понятно... По-моему так никто не делает" Я так и не делаю, я методом дедукции додумался до такого: t = get_timer1(); if(t>30000){t1++ ;} if(input(PIN_E0)) { if(t1==6){speed = 2; x = (x-speed);} if(t1>1200){speed = 10; x =(x-speed); t1 = 1190;} } else if (input(PIN_E1)) { if(t1==6){speed = 2; x=(x+speed);} if(t1>1200){speed = 10; x=(x+speed); t1 = 1190;} } else {speed = 1; t1 = 1; }
но при симуляции значения меняются не плавно, а как то рывками Задача одиночного нажатия если упрощенно: изменить значение ШИМ один раз сколько б не удерживалась кнопка в течении 1сек. С прерыванием мне видится что-то следущее:
#INT_RTCC VOID TIMER_INTERRUPT (void) { if (flag == 0) { pwmDuty = ( pwmDuty + 1) } else flag += 1 if (flag==200){ flg = 0 } } То есть изменение периода ШИМ будет происходить один раз из 200. Так правильно? Или имеется ввиду внешнее преоывание? А как на СИ назначить прерывания от разных кнопок? Значение 200 взято просто для примера, 1сек расчитать не сложно. А как добавить нажатие более 1сек ? Совсем мозги всем запудрил Есть ли стандартные процедуры обработки нажатий кнопок? А то может я зря придумываю всякую муть _.
|
|
|
|
|