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

Ликбез по программированию PIC

1 55 99

Zandy: Я вот до сих пор не понимаю, как обрабатывать 4 канала, кроме как не по очереди в принудительном порядке. Ну а между обработками со светодиодами валандаться. Каким образом сюда прерывания приплести? Вот если бы мы могли разрешать прерывания отдельно по каждому из входов, тогда бы все было предельно просто.

Не по очереди, а по мере поступления прерываний
Пришло прерывание от изменения уровня - выяснили от какого сигнала - записали длительность (разность между текущим TMR и предыдущим по этому каналу)... В принципе, в прерывании можно больше ничего не делать, остальной анализ производить вне прерывания, в основной программе.
Или, если нужно осреднение, то в прерывании полученная длительность прибавляется к сумме длительностей по данному каналу, и увеличивается счётчик импульсов данного канала. По достижении счётчиком нужного значения (например, 8 ), производится деление суммы на счётчик, запись результата в переменную-результат по каналу, и обнуление суммы и счётчика...
Примерно так

 

Идея мне представляется примерно так. Источников прерывания у нас возможно пять. Это таймер тайм-аута и четыре входа. Вектор прерываний у PIC16 один, поэтому необходимо в обработчике анализировать флаги запросов и выявлять источник. Порядок анализа флагов запроса и будет определять приоритеты прерываний.
Прерывание от таймера тайм-аута должно иметь наивысший приоритет. Поэтому первым делом проверяем флаг запроса от таймера тайм-аута. Если он установлен - переходим к выполнению действий, необходимых в этом случае. Если нет - идём дальше.
Далее проверяем флаг RBIF. Если и он не установлен, значит это ложное прерывание.
Если же флаг RBIF установлен, то анализируем - на каком именно или на каких именно входах произошло изменение уровня.
Считываем PORTB и сохраняем в INPUT_NOW, замаскировав ненужные биты.
Сравниваем INPUT_NOW c INPUT_OLD (с предыдущими уровнями на входах).
Вот так одним махом и выявляются хоть все четыре изменившихся уровня.
Копируем новые считанные данные из INPUT_NOW в INPUT_OLD, где они и будут храниться до следующего раза.
А далее проверяем каждый бит.
Состояние RB4 изменилось И на RB4 лог.1? Да - действия для этого случая я уже описал выше, вчера. Состояние RB4 изменилось И на RB4 лог.0? Да - опять же смотрим вчерашний разбор. Состояние RB4 не изменилось - идём дальше.
Состояние RB5 изменилось И на RB5 лог.1? Состояние RB5 изменилось И на RB5 лог.0? Состояние RB5 не изменилось? (Аналогично)
И так для всех входов.
Для каждого входа потребуются свои регистры TMRTEMP и RESULT (например, TMRTEMP4 и RESULT4 для RB4). Не стоит опасаться того, что мы задействуем много регистров ОЗУ. Именно для этого они и нужны. Мы сами ведь не прыгаем на одной ноге - у нас есть две, пусть обе и работают. И все десять пальцев используем. Вот и регистры ОЗУ пусть выполняют свои функции, излишняя экономия здесь не нужна.
Таким образом, используя единственный таймер, инкрементируемый импульсами заполнения, мы сможем получить в регистрах RESULT длительности периодов на всех входах.
Время работы обработчика должно быть как можно меньше.
Это предложение "навскидку", поэтому прошу внимательно анализировать и критиковать. Я вполне мог чего-то не учесть.

Насчёт действий при выходе по тайм-ауту я ещё подумаю.

PS: пока набирал текст, АНТОХА уже запостил.

 

picmaniac: Респект АНТОХЕ за ссылку.

Это скорее респект chav1961 и гуглю

 

picmaniac: Состояние RB4 изменилось И на RB4 лог.1? Да - действия для этого случая я уже описал выше, вчера. Состояние RB4 изменилось И на RB4 лог.0? Да - опять же смотрим вчерашний разбор.

Здесь можно упростить. Не анализировать уровень, а просто считать чётное к-во импульсов. Например, 16 импульсов - это 8 периодов...

 

Splav56: Единственный вопрос может возникнуть при многоканальной работе в случае синфазности сигналов.
Короче, мы этого не боимся и "кролика в рукаве" у нас нет тоже.
Ну что, можно пытаться программу писать или будем граф составлять?
А все-таки, почему нельзя принудительно, по очереди опрашивать все каналы без всяких прерываний?

 

Почему же нельзя? Распишите свою идею подробнее, обсудим...

Диалог автора и разработчика на стр.484-485 прочитали? Лучше, конечно, граф сначала составить. Хотя бы вчерне. Хотя бы даже не граф, а поясняющий рисунок. Но если совсем невтерпёж - можно и программу писать.

 

Zandy: А все-таки, почему нельзя принудительно, по очереди опрашивать все каналы без всяких прерываний? ИМХО похоже единственный вариант,вот и дошли до задачки, где для имеющихся (заданных) ПИКовых ресурсов, если строго по заданию, уже не все просто...

 

picmaniac: Почему же нельзя? Распишите свою идею подробнее, обсудим...
Вот, расписываю.

Период 25Гц - 40мс. 8периодов - 320мс. Наша самая быстрая мигалка - 2Гц. Значит зажигать и гасить светодиод нам надо через каждые 250мс. Не вяжутся как-то эти времена между собой. Программа, занятая подсчетом длительности не сможет обрабатывать мигание светодиода. По-моему такой вариант не годится.

Напрашивается такой вариант. В каждом цикле программы измерять только один период и записывать его в соответствующие регистры 1R1 - 1R8. В первом цикле записываем в 1R1. В следующем цикле программы, когда будет произведено измерение периода по остальным трем каналам и мы вернемся опять к первому, измеренный результат запишем уже в 1R2. И так до тех пор, пока не запишем 1R8. На 9-ом цикле длительность будет записана опять в 1R1 и т. д. Сразу после измерения периода и записи ее в регистр осуществляем математическое усреднение по всем 8-и регистрам первого канала и записываем результат в регистр PERIOD1. Таким образом усреднение будет вестись по текущему и семи предыдущим отсчетам. Сразу после этого - оценка периода (сравнение с константами длительности). Результаты оценки (4 возможных варианта) записываем в регистр REZ1 (4 бита). Таким образом идет обработка по всем четырем каналам.

Остановимся теперь на программе измерения периода. Допустим, будем использовать аппаратный таймер. В начале измерения его обнуляем. Затем организуем цикл опроса вывода порта. Записываем его состояние в промежуточный регистр. На следующем цикле опроса порта сравниваем текущее значение с записанным. Т. о. "ловим" либо передний, либо задний фронт импульса. Когда он "пойман" еще раз обнуляем таймер и ждем в цикле опроса, когда придет следующий фронт. По этому событию считываем состояние таймера и записываем в один из регистров 1R1 - 1R8, как было рассмотрено выше. Теперь тонкость. Зачем мы обнулили таймер перед началом опроса? А это для того, чтобы в случае, если ожидаемый импульс так и не придет, спокойненько выйти из цикла опроса кнопки по переполнению таймера. Таймер должен быть расчитан на измерение максимально возможной длительности.

Теперь посмотрим и заметим, что время присутствия программного вектора в области команд опроса и обработки одного канала не будет все время постоянным, оно будет зависеть от различных вариантов поведения входного сигнала. Приблизительно это время будет составлять около 45мс. А нам необходимо, чтобы это время было постоянным. Зачем - будет понятно потом. Для того, чтобы это сделать, задействуем, например, еще один таймер. В начале опроса мы его инициализируем, а в конце всех действий с каналом войдем в цикл опроса этого таймера и когда он насчитает нам ровно 62мс, перейдем к работе со следующим каналом.
Итак, мы будем иметь общий большой цикл работы с четырьмя каналами 62 х 4 = 250мс. Один раз за этот цикл будем инвертировать биты в каком-нибудь специально отведенном для этого регистре. Вот вам и "мигалка" 2Гц. Еще один регистр, инвертирующийся один раз за 4 цикла - мигалка 0.5Гц.

Теперь вернемся к обработке канала. После того, как мы получили результаты оценки (REZ1), выведем эти результаты на соответствующие светодиоды. Варианты: горит, погашен, "подсоединен" к мигалке 2Гц или 0,5Гц.

Конечно я здесь не затронул многие "тонкости" программы, но и так уже много понаписал. Как вариант - можно обрабатывать каналы и в прерываниях по "тому" второму таймеру.

 

Если я правильно понял, предлагается опрашивать входы поочерёдно. По 62 мс на опрос каждого входа. Из этих 62 мс примерно 45 мс ждём изменения уровня на входе. А как насчёт остальных (4*62)-45 ~= 200 мс? За это время изменение уровня на входе как отслеживается? И как оно отслеживается в промежутке от 45 до 62 мс? И в какое время заниматься математикой, и как во время этого вход отслеживать?
Изменение уровня на любом входе может произойти внезапно, в любой момент, а не только тогда, пока мы его ждём.
Реализация программы с прерываниями по изменению уровня на входах позволяет "ловить" изменение уровня на любом входе (хоть на одном, хоть на двух, трёх, всех одновременно) в произвольный момент времени. Реакция устройства будет немедленной. Независимо от того, чем контроллер в этот момент занимался. На то оно и прерывание. А пока контроллер аппаратно ожидает прерывания, можем загрузить его, к примеру, математическими расчётами, организацией мигания индикаторов и т.п. Обработчик прерывания от входов отрабатывает настолько быстро, что не внесёт видимой погрешности в период миганий. Отработал, выдал результат - и можем этот результат спокойно обрабатывать, ожидая следующего прерывания.
Момент нечувствительности в этом случае исчезающе мал - от момента опроса порта до момента сброса флага запроса на прерывание по изменению уровня. Допустим, изменения уровня на двух входах происходят почти одновременно. По первому изменению устанавливается флаг RBIF. Если второе изменение произошло до момента опроса порта - мы его при опросе уже "отловим". На промежуток от момента опроса порта до сброса флага RBIF изменения на входах контроллеру не видны. Если же второе изменение происходит после сброса нами флага RBIF, то этот флаг немедленно установится опять. И после выхода из обработчика тут же произойдет его повторный вызов.
Так вот:
movf PORTB,W
bcf INTCON, RBIF
movwf MYREG
зона нечувствительности получается всего лишь порядка 1 мкс, что не представляет угрозы. И вообще-то я ещё подумаю, а что произойдет, если изменение на входе случится именно в момент нечувствительности... и точно ли контроллер этого не заметит...

 

"Правки" уже нет, но хотелось бы дописать. Непонятным остается поведение системы в следующем случае. Например, скорость встала на границе двух диапазонов измерения (с/д горит и с/д мигает с частотой 0.5Гц). В этом случае при каждой обработке канала за счет непостоянства (гуляния) скорости, мы будем ставить светодиод или в состояние "горит постоянно" или "мигает 0.5Гц". В результате мигание с/д будет непредсказуемым и отличным от наших двух вариантов, что может быть неправильно воспринято и истолковано оператором. Я уже говорил об этом раньше, но комментариев по этому поводу не было. Я бы сделал здесь какую-то зону нечувствительности при анализе периода, ну типа гистерезиса. Как это сделать, и надо ли это делать - надо думать. Прошу высказаться.