Я прошу прощения, но это настолько маленькая вещь, что 2 файла скопировать, я думаю, труда не составит. Я сделаю библиотеку, но несколько позже, когда отлажу очередь сообщений, и то, что я выложил сейчас, будет лишь ее маленькая часть.
Желательно, что бы ссылка на функцию Код (C++): using PVoidFunc = void(*)(void); Принимала указатель на область памяти. Тогда будет возможность одну функцию вешать на несколько таймеров. И использовать в библиотечных функциях явную конструкцию Код (C++): cli(); ... ... sei(); Не желательно - потому, что если вы зайдете в функцию с выключенным флагом прерывания то функцию в любом случае прерывания включит (в step у Вас так и происходит). Часто приводит к очень не хорошим глюкам. Лучше сохранять значение флага прерываний, а потом восстанавливать.
Ни кто не гарантирует, что функции всех таймеров выполнятся в течении цикла таймера (вроде у Вас 1 милисекунда). Поэтому вызов Код (Text): Items[i].CallingFunc(); Просто необходимо вынести из прерывания. Я обычно делаю примерно так: Код (C++): void loop(void) { ... for (THandle i = 0; i < MAXTIMERSCOUNT; i++) { if (Items[i].WorkingCounter == 1) { Items[i].WorkingCounter = 0; // Таймер выключен (если передавать ссылку на //таймер в функцию вызова, она сможет его включить) Items[i].CallingFunc(); } } }
Никто не мешает и сейчас вешать одну функцию на несколько таймеров с разными интервалами по второму замечанию (по прерываниям) - ничо не понял. Они запрещаются на время изменения массива, и вызова нужной функции, да, ну так и функция отклика должна быть короткой (как мне кажется). У меня она используется например для проверки датчиков, и если показания изменились - положить сообщение в очередь сообщений. 16 МГц процессор за 1 миллисекунду выполнит более 10000 команд, думаю, никто такие раздутые функции отклика писать не будет функция перебора и изменения массива должна быть атоммарной, иначе будут глюки точно. А вот перед вызовом CallFunc прерывания можно и разрешить, а запрещение перенести в начало цикла примерно так Код (C++): for (THandle i = 0; i < MAXTIMERSCOUNT; i++) { cli(); if (Items[i].CallingFunc == NULL) continue; if (NOT Items[i].Active) continue; if (--Items[i].WorkingCounter > 0) continue; Items[i].WorkingCounter = Items[i].InitCounter; sei(); Items[i].CallingFunc(); }
И это... Если прерывания были запрещены до этого, мы в функцию перебора таймеров не попадем, ибо Step() вызывается именно из аппаратного прерывания таймера. А на выходе из аппаратного прерывания, они всё равно разрешаются командой reti.
Согласен никто не мешает. Я имею ввиду следующее: В упрощенном случае можно представить, что одна и та же функция используется для зажигания разных диодов в разное время. Тогда в качестве параметра нужно передавать номер пина и порт. А в более общем случае указатель на некоторый объем данных. Раздутые и не нужно писать - вывод в RS232 одного символа при скорости 9600 займет ровно одну мили секунду, конечно там два байта ложатся в буфер, но таймеров то 8. Три байта в порт и время истекло. Точно не проверял но мне кажется Serial.print не асинхронная функция. Менее явный вариант, если кто-нибудь в прерывание начнет выполнять операции деления например. Ну а вообще от проекта зависит, когда я пишу проект полностью я знаю какую функцию я могу выполнять в прерывании, а какую нет, но к сожалению большинство даже достаточно опытных разработчиков не в состоянии разобраться.
Воот, и вот тут мы плавненько подходим к следующему этапу, который я выложу скоро, к очереди сообщений. Как в виндовсе, проверил по таймеру, например, датчик температуры, не изменилась - на выход , изменилась - кладем в очередь команду с параметрами (в общем случае - указатель на данные) перерисовать экран, например, или включить/выключить реле и т.д и опять же на выход. Тогда loop() будет состоять из выборки сообщений и управления исполнительными механизмами. И ничего лишнего, никаких if millis()-prevmillis , есть событие, приходящее асинхронно, есть отклик, выполняющийся последовательно, на следующей итерации loop(). Пока тестирую, но уже получается красиво.
Ничего не займет, ибо тоже работает по прерываниям опустошения/переполнения буфера. Иначе бы проц только этим и занималса. С точки зрения процессорного времени, передача одного символа через Serial - это примерно как для нас промежуток времени от аванса до зарплаты, мы же не сидим всё это время сложа руки. Вот и у него есть специальные прерывания UART, их даже несколько Про софт сериал такого не скажу.
Единственное, что портит картину - analogRead(), который выполняется достаточно долго, сссскатина. Над этим я тоже счас думаю.
Как один из вариантов решения - http://forum.amperka.ru/threads/Простые-примеры-adc-1-wire.6438/. Это без библиотеки, где-то на работе была библиотека. В сети очень много примеров, библиотек выбирайте на свой вкус.