Так, я так понял что MsTimer2 позволяет только одно прерывание делать?
Пока ответа не нашел - подключил Metro.h, но здесь тоже вопрос возник, хотя, может и не по душу этой библиотеки. Привожу текущий код:
Вопрос: Почему мне пришлось вынести int EngineCurrentVentPWM в глобальные переменные, иначе не работает плавная регулировка... тупо стоит на 0.PHP код:
/*
Программа термоконтроля за системой охлаждения ДВС. Использует плавную регулировку по ШИМ.
Может применяться и в других проектах, где исходные данные будет выдавать термистор (терморезистор).
В качестве исходных данных - 1 проводной температурный датчик (термистор(терморезистор))
двигателя с которого берется информация на штатную приборную панель автомобиля.
Т.к. датчик имеет нелинейную зависимость сопротивления (падения напряжения),
то необходима аппроксимация по заранее известным точкам. Чем выше температура,
тем ниже сопротивление и больше падение напряжения.
Для достоверной работы программы нужно ввести значения проходящего через датчик напряжения при
различных температурах, желательно во всем диапазоне его работы.
За аппроксимацию спасибо SBorovkov.
*/
#include <Metro.h> //Библиотека регулярных действий по интервалу времени
#include <MyLiquidCrystalRus.h> //Библиотека ЖК дисплея
MyLiquidCrystalRus lcd(12, 11, 10, 5, 4, 3, 2); //Используем ЖК дисплей
#include <avr/pgmspace.h> //Подключаем макрос PROGMEM
//*********************Параметры для работы термоконтроля ОЖ двигателя
#define EngineTemp1Count 14 //Количество точек аппроксимации
#define DestinationEngineTemp 90 //Определяем нужную температуру двигателя
#define EngineTempSensorPin 0 // Аналоговый вход для температурного датчика ОЖ ДВС
#define EnginePowerVentPin 6 //ШИМ выход на вентиляторы основного радиатора ДВС
//Задаем точки аппроксимации. Первая цифра - разность из 1024 и значения на входе от датчика
//(сделано для того чтобы развернуть график зависимости и сделать так чтобы с ростом температуры значение увеличивалось)
//не переводил в напряжение чтобы не морочиться и оперировать целыми числами с достаточной точностью;
//вторая цифра - значение температуры (заранее измеренное) при данном значении на выходе с датчика.
uint16_t EngineTemp1[EngineTemp1Count][2] PROGMEM = {{0,0}, {471,46}, {635,56}, {702,64}, {723,71}, {729,75}, {764,80}, {793,85}, {811, 90}, {836, 95}, {862, 102}, {872, 110}, {882, 120}, {890, 130}};
//************************Процедуры по таймеру*************************
Metro PWMengineMetro = Metro(10); //Плавное изменение мощности вентиляторов каждые ХХ миллисекунд
Metro LCDoutMetro = Metro(250); //Выводим информацию на ЖК каждые ХХ миллисекунд
int EngineCurrentVentPWM; //Текущая мощность вентиляторов ОЖ двигателя
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("****************");
lcd.setCursor(0,1);
lcd.print("****************");
delay(1000);
}
void loop() {
//**********************Расчет всех параметров на основе температуры ОЖ двигателя
int EngineTargetVentPWM; //Целевая точка мощности вентиляторов ОЖ двигателя
// int EngineCurrentVentPWM; //Текущая мощность вентиляторов ОЖ двигателя
int EnginePowerVentPercent; //Мощность вентиляторов на ОЖ двигателя в %
int EngineTargetVentPercent; //Задаваемая мощность вентиляторов на ОЖ двигателя в %
uint16_t EngineRealTemp; //Расчетное значение реальной температуры ОЖ двигателя
int i;
int EngineCount = 20; //Количество значений для усреднения
int EngineIn = 0; //Начальная точка суммирования
int EngineInSrednee; //Усредненное значение со входа
uint16_t EngineInTemp; //Входное значение с датчика температуры
uint16_t EngineInLeftTemp;
//Цикл усреднения значения
for (i = 0; i < EngineCount; i++) {
EngineIn = EngineIn + analogRead(EngineTempSensorPin);
}
EngineInSrednee = EngineIn / EngineCount; //Вычисляем среднее значение
EngineInTemp = 1024 - EngineInSrednee; //Переворачиваем график падения напряжения
i=EngineTemp1Count;
do
{
i--;
EngineInLeftTemp=pgm_read_word(&EngineTemp1[i][0]);
}
while ((i>=0)&&(EngineInTemp<EngineInLeftTemp));
uint16_t EngineInRightTemp=pgm_read_word(&EngineTemp1[i+1][0]);
uint16_t EngineOutLeftTemp=pgm_read_word(&EngineTemp1[i][1]);
uint16_t EngineOutRightTemp=pgm_read_word(&EngineTemp1[i+1][1]);
EngineRealTemp=EngineOutLeftTemp+(EngineOutRightTemp-EngineOutLeftTemp)*(EngineInTemp-EngineInLeftTemp)/(EngineInRightTemp-EngineInLeftTemp); //Вычисление значения реальной температуры
float EngineVoltIn = (5.00 / 1024.00 * EngineInSrednee); //Переводим входные данные к абсолютному значению напряжения для мониторинга
//Если температура входит в определнные значения - запускаем вентиляторы на определенную мощность.
if (EngineRealTemp < DestinationEngineTemp - 5) EngineTargetVentPWM = 0;
if (EngineRealTemp >= DestinationEngineTemp - 5) EngineTargetVentPWM = 36;
if (EngineRealTemp >= DestinationEngineTemp - 4) EngineTargetVentPWM = 72;
if (EngineRealTemp >= DestinationEngineTemp - 3) EngineTargetVentPWM = 108;
if (EngineRealTemp >= DestinationEngineTemp - 2) EngineTargetVentPWM = 144;
if (EngineRealTemp >= DestinationEngineTemp - 1) EngineTargetVentPWM = 180;
if (EngineRealTemp >= DestinationEngineTemp) EngineTargetVentPWM = 216;
if (EngineRealTemp >= DestinationEngineTemp + 1) EngineTargetVentPWM = 255;
EnginePowerVentPercent = 100.00/255.00*EngineCurrentVentPWM; //Вычисляем значение в процентах текущей мощности вентиляторов
EngineTargetVentPercent = 100.00/255.00*EngineTargetVentPWM; //Вычисляем значение в процентах задаваемой мощности вентиляторов
//**********************Процедура плавного изменения мощности вентиляторов
if (PWMengineMetro.check() == 1) {
if (EngineCurrentVentPWM > EngineTargetVentPWM && EngineCurrentVentPWM > 0){
EngineCurrentVentPWM--;
analogWrite(EnginePowerVentPin, EngineCurrentVentPWM); // Уменьшаем мощность
}
if (EngineCurrentVentPWM < EngineTargetVentPWM && EngineCurrentVentPWM < 255)
{
EngineCurrentVentPWM++;
analogWrite(EnginePowerVentPin, EngineCurrentVentPWM); // Увеличиваем мощность
}
}
//***********************Выводим все что нам нужно на ЖК дисплей
if (LCDoutMetro.check() == 1){
// lcd.clear();
lcd.setCursor(0,0);
lcd.print("ВЛТ:");
lcd.setCursor(4,0);
lcd.print(EngineVoltIn); //Входное напряжение с датчика
lcd.setCursor(8,0);
if (EngineVoltIn > 2.70) {
lcd.print(" ТМП:");
lcd.setCursor(13,0);
lcd.print("MIN"); //Ниже минимальной точки аппроксимации
}
else if (EngineVoltIn < 0.65){
lcd.print(" ТМП:");
lcd.setCursor(13,0);
lcd.print("MAX"); //Выше максимальной точки аппроксимации
}
else {
lcd.print(" ТМП:");
lcd.setCursor(13,0);
lcd.print(EngineRealTemp); //Аппроксимированная температура
lcd.print(" ");
}
lcd.setCursor(0,1);
lcd.print("МОЩНОСТЬ:");
lcd.setCursor(9,1);
lcd.print(EnginePowerVentPercent); //Мощность вентиляторов
lcd.print(" ");
lcd.setCursor(12,1);
lcd.print("%");
lcd.setCursor(13,1);
lcd.print(EngineTargetVentPercent); //Заданная мощность вентиляторов
lcd.print(" ");
}
}
Ну и так, посмотрите, правильно ли двигаюсь? Может что-то можно оптимизировать или сделать более рациональным?
Последний раз редактировалось paranom; 17.08.2010 в 03:24.
С уважением, Дмитрий.
тел. 8 9I6 855 7Ч 95
Скажи, ты раньше на чем-то програмил? Уж слишком быстро разбираешься!
Теперь ответы :-)
У самого микроконтроллера AtMega168 три таймера. Но насколько я понял, как минимум один, и может два из используется встроенными структурами, которые обеспечивают работу систем Arduino. Итого - старайся выкрутиться одним таймером. Кстати, при грамотной его обработке его хватит в 90% не очень сложных случаев.
К примеру, есть функции, которые надо вызывать каждые 50мс, а есть такие, которые надо вызывать каждую секунду. Ставишь таймер на 50мс и каждый 20 вызов таймера вызываешь ежесекундную функцию.
По поводу переменной EngineCurrentVentPWM: Если ты объявил переменную глобальной (вне каких-либо функций) она существует все время выполнения программы. Соответственно, она хранит значение все время, пока программа выполняется.
Если ты объявил переменную внутри функции, то эта переменная существует только внутри функции (либо еще меньше). Как код только из функции вышел, переменная исчезла и не занимает память. Когда произойдет следующий вход в функцию, переменная будет заново создана и будет иметь случайное значение, если ты ее не проинициализируешь.
Соответственно, твоя функция loop вызывается постоянно. Но при каждом вызове все объявленные внутри переменные создаются заново! И их надо инициализировать, прежде чем из них что-то считать. Кстати, компилятор тебе наверняка выдавал предупреждения на счет чтения неинициализированных переменных.
Когда ты перенес переменную EngineCurrentVentPWM вне loop, ты создал ее глобальной - она существует все время выполнения программы (т.е. вечность, поскольку loop вызывается вечно).
Попробую объяснить понятнее про область видимости.
Существует четыре области видимости: локальная, функция, файл и класс.
Локальная:
Есть блок, ограниченный фигурными скобками
{
...
int variable
...
}
Переменная существует с момента объявления и до закрывающей скобки блока.
Как только скобка закрылась, переменная исчезла. Вместо троеточия может быть любой код. В том числе содержащий еще блок {...}
Область видимости - функция
int func(void)
{
...
int variable;
...
}
В таком случае переменная существует от момента определения и до конца функции. Как только функция завершила свою работу, переменная перестала существовать.
Глобальная функция, область видимости - файл.
int Counter;
void loop()
{
Counter++;
}
Переменная существует все время выполнения программы.
Пример -
Переменная Counter объявлена глобальной. Соответственно, к ней можно обратиться в обеих функциях и переменная будет чество хранить значение.Код:int Counter; void Init(void) { Counter=5; } void loop(void) { // 1 Counter++; int i; for (i=0;i<5;i++) { //2 int j; j=i*2; if (j=10) { //3 int k; k=j+5; if (k>12) { //4 .... } //4 } //3 } //2 } //1
Переменная i объявлена с областью видимости - функция. К ней нельзя обратиться в фенкции init. Более того, значение переменной i в момент входа в функцию не определено и может быть абсолютно любым! К этой переменной можно обратиться в любом месте функции loop - с момента ее объявления и до конца выполнения функции, пока не закроется скобка с номером 1.
Переменные j и k объявлены с локальной областью видимости. Переменная j существует пока процессор не выполнит закрывающую скобку 2. Переменная k - пока процессор не выполнит закрывающую скобку 3. Соответственно, при КАЖДОМ входе в цикл, переменная j имеет неизвестное значение.
А теперь разумный вопрос - а зачем это все!?
Ответа два:
1. не всегда хочется, чтобы в любом месте кода были видны все остальные переменные.
К примеру, пишешь мелкую функцию, внутри нее цикл от 1 до 10, она сделала свое дело и закончила работу. Она асболютно сепаратная - никого не волнует как у нее внутри называется переменная цикла. И если переменную цикла объявить внутри самой функции, то в результате переменная исчезнет по выходу из функции. Таким образом в каждой функции твоего модуля может быть определена переменная i и в каждой функции эта переменная будет своя!
2. Компилятор при смотри на область видимости и соответственно размещает переменные в памяти. Вышли из области видимости переменной - память, занимаемую переменной освободили. Нужно занять под новую переменную - пожалуйста, снова выделит.
Кстати, компиляторы нынче шибко умные...
К примеру, компилятор понимает, что функция func - ничего не делает, просто перекладывает локальные переменные туда-сюда. И вообще не включает эту функцию с результат компиляции. Соответственно, вызова func тоже нет...
Код:void func(void) { uint8_t i; uint8_t j=0; for (i=0;i<10;i++) j=j+i; uint8_t k; k=j+1; for (i=0;i<10;i++) k=k+i; } int main(void) { func(); }
Спасибо, огромное! Теперь стало многое понятным.
Ты знаешь, жизнь слишком коротка чтобы что-то в ней не понять. И тяжелее понять людей, чем машину ))) Если серьезно - то что есть опыт в радиотехнике и логических схемах в частности (в детстве баловался элементарной логикой и регистрами сдвига) + бэйсик со времен zxspectrum + php )))Сообщение от SBorovkov
С областью видимости переменных я все понял.Сообщение от SBorovkov
Вот здесь мне не совсем понятно... EngineCurrentVentPWM у меня, по идее, может существовать только в рамках void loop, т.к. везде, где переменная используется находится в рамках функции loop. Так какого ей надо в глобали? И что значит проинициализировать?Сообщение от SBorovkov
С уважением, Дмитрий.
тел. 8 9I6 855 7Ч 95
На самом деле программа arduino выглядит примерно так:
int main(void)
{
Init()
Setup();
for (;
loop();
}
Предположим ты объявил переменную EngineCurrentVentPWM внутри loop().
В этом случае, каждый раз, когда выполнение заходит в выполнение loop() (а он это делает как только завершится прошлый вызов loop, согласно коду выше), выделяется место под переменную EngineCurrentVentPWM и эта переменная каждый раз имеет некоторое, заранее неопределенное, значение. Каждый раз, когда код выходит из loop, память под все переменные функции loop освобождается.
Проинициализировать = присвоить значение.
Считай, что да. В небольших программах переменные, в которых хранится некое текущее состояние, статус, настройки и тому подобное часто делают глобальными.
Понял. Спасибо. Продолжу сегодня работу и, скорее всего, вынесу результат в отдельную тему, т.к. Могут возникнуть вопросы еще по железу.
С уважением, Дмитрий.
тел. 8 9I6 855 7Ч 95
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)