Свежие обсуждения
Микроконтроллеры

Ликбез по С для микроконтроллеров PIC

1 40 64

Сергей К: У меня размер кода уже вырос до 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сек ?

Совсем мозги всем запудрил Есть ли стандартные процедуры обработки нажатий кнопок? А то может я зря придумываю всякую муть _.