Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Структура программы
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
UniBomb
Добрый день. Возник у меня вопрос, который терзает моё эго и не даёт спокойно работать. Вопрос заключается в организации программы. В общем вот:

Допустим есть ТЗ: сделать прибор, который должен:

- считывать температуру с датчика по шине TWI
- считывать значение напряжения с АЦП с датчика влажности
- расчитывать значение влажности в общепринятых единицах
- выдать предупреждающие сигналы в случае превышения норм
- показать значения на дисплее

Зада просто и понятна (хоть и высосана из пальца). Реализовал бы всё это дело я так:

Код
#include <io.h>
volatile int counter;
int main()
{
//инициализирую всё и вся

for(;;) //пустой бесконечный цикл
;
}

ISR(SIG_OUTPUT_COMPARE1A)
{
switch(counter)
  {
   case 100: BeginTempConvers(); break; //посылаю запрос для начала считывания температуры
   case 200: GetADCValue(); break; //считываю значение с АЦП
   case 300: ProcessADCValue(); break; //раситываю необходимые значения
   case 400: GetTempValue(); break; //считываю температуру
   case 500: SetAlarm(); break; //проверяю значения и вывожу аварийные сигналы если надо
   case 600: DisplayValues(); break; //вывожу все данные на экран
  };

counter++;
if(counter == 601)
  counter = 0;
}


Так вот вопрос - хорошо ли я делаю? Какие есть ещё способы реализации поставленной задачи. Просто мне почему то кажется, что всю логику работы прибора нехорошо запихивать в таймер...
Палыч
Цитата(UniBomb @ Mar 18 2009, 14:33) *
Просто мне почему то кажется, что всю логику работы прибора нехорошо запихивать в таймер...
Очень правильно кажется! Желательно как-то так: в таймере выставлять флаги операций, а в бесконечном цикле (тот, что в main) - выполнять соответствующие операции по флагам.
XVR
Все в таймер явно нехорошо smile.gif Можно сделать так - в прерываниях обрабатывается взаимодействие с датчиками (самое простое, без дальнейшей обработки). Вся основная логика реализуется в процедуре, которая циклически вызывается из процедуры UI (какой бы он не был - кнопки и индикатор или RS232 к PC).
Если логика не тривиальная - делается один или более взаимодействующих конечных автомата.
UniBomb
Цитата(Палыч @ Mar 18 2009, 14:40) *
Очень правильно кажется!

Кстате, а почему плохо? Спрашиваю для общего развития, а не для спора)))


Цитата(Палыч @ Mar 18 2009, 14:40) *
Желательно как-то так: в таймере выставлять флаги операций, а в бесконечном цикле (тот, что в main)


Код
#include <io.h>
volatile int counter;
int main()
{
//инициализирую всё и вся

for(;;) //уже не пустой бесконечный цикл
  {
   switch(counter)
   {
    case 100: BeginTempConvers(); counter++; break; //посылаю запрос для начала считывания температуры
    case 200: GetADCValue(); counter++; break; //считываю значение с АЦП
    case 300: ProcessADCValue(); counter++; break; //раситываю необходимые значения
    case 400: GetTempValue(); counter++; break; //считываю температуру
    case 500: SetAlarm(); counter++; break; //проверяю значения и вывожу аварийные сигналы если надо
    case 600: DisplayValues(); counter++; break; //вывожу все данные на экран
   } //counter инкрементируется для того, что бы функция невызывалась повторно в случае когда время итерации цикла меньше перода таймера
  }
}

ISR(SIG_OUTPUT_COMPARE1A)
{
counter++;
if(counter == 601)
  counter = 0;
}


ммм.... так?


Цитата(XVR @ Mar 18 2009, 14:42) *
Все в таймер явно нехорошо smile.gif Можно сделать так - в прерываниях обрабатывается взаимодействие с датчиками (самое простое, без дальнейшей обработки). Вся основная логика реализуется в процедуре, которая циклически вызывается из процедуры UI (какой бы он не был - кнопки и индикатор или RS232 к PC).
Если логика не тривиальная - делается один или более взаимодействующих конечных автомата.

Да логика то в моих реальных заданиях такая же простая как в примере))) А начсёт процедур - что за процедура UI?
Палыч
Цитата(UniBomb @ Mar 18 2009, 16:50) *
Кстате, а почему плохо? Спрашиваю для общего развития, а не для спора
Процедуры обработки прерывания желательно писать так, чтобы время выполнения её было минимальным. Причин несколько. Формулировать все - лень... Например, время обработки прерывания от таймера превысит интервал таймера. Что будет?

Поскольку Ваша разача сводится к периодическому выполнению одной и той же последовательности действий, то, наверное, лучше как-то так:

Код
#include <io.h>
volatile char flag;
int main()
{
//инициализирую всё и вся
flag= 0;
for(;;) //уже не пустой бесконечный цикл
  {
   if(flag)
   {
     flag= 0;
     BeginTempConvers(); //посылаю запрос для начала считывания температуры
     GetADCValue();  //считываю значение с АЦП
     ProcessADCValue();  //раситываю необходимые значения
     GetTempValue();  //считываю температуру
     SetAlarm();  //проверяю значения и вывожу аварийные сигналы если надо
     DisplayValues();  //вывожу все данные на экран
   }
  }
}

ISR(SIG_OUTPUT_COMPARE1A)
{
  flag= ~0;
}


P.S. Пардон, не всё лишнее убрал из программы. Поправил...
_Pasha
Цитата(UniBomb @ Mar 18 2009, 16:50) *

Возьмите в начале проверьте, не изменился ли counter
затем делайте switch(counter)


Упс... counter - он ведь у Вас неатомарный при чтении...
DpInRock
Прежде чем применять (или не применять) стиль программирования следует определиться с алгоритмом. Иначе применение (равно как и не применение) стиля - занятие исключительно для развлечения.

Кстати, вполне можн все это дело разместить в прерывании от таймера, если таймер заменить на вочдог на несколько секунд, а в цикле фор зациклить слип.

Вот тогда устройство можно запитывать от батарейки. Уже какая-то польза будет.
В таймере можно запросто размещать то, что ПРОСТО ОБЯЗАНО выполнятся строго регулярно. И если это чт-то не успевает, значит задача не решаема в таком виде.

А нужен стиль или не нужен - это станет понятно при повышении уровня сложности задач. В тех задачах, которые не начинаешь с запуска среды компилятора, с поиска долбаннго листка бумаги и хоть какой-нибудь ручки.
haker_fox
Цитата(UniBomb @ Mar 18 2009, 19:33) *
Добрый день. Возник у меня вопрос, который терзает моё эго и не даёт спокойно работать. Вопрос заключается в организации программы.

Здравствуйте!
В прерываниях обрабатывайте то, что там обрабатывать нужно. Обработчики прерываний делайте максимально быстрыми. Весь функционал программы (обработки информации с датчиков, выдача данных на дисплей, обработка данных от клавиатуры) разместите в отдельных функциях, которые вызывайте из бесконечного цикла в main(). А вот работу с шиной I2C, сбор данных с АЦП можно (и даже лучше) разместить прерывания. Таким образом, в обработчике прерывания от АЦП Вы будете считывать данные и ложить их в буфер, а также анализировать ошибки АЦП. Все. В обработчики прерываний шины TWI вы будете анализировать состояние шины, ошибки, принимать данные и ложить в буфер или брать данные из буфера и передавать их. Все. А вот ложить данные в буферы, читать их оттуда, уже задача основной программы. Ну примерно так. Дальше уже с опытом понимание придет.

Цитата(UniBomb @ Mar 18 2009, 21:50) *
Кстате, а почему плохо? Спрашиваю для общего развития, а не для спора)))

Тут дискутировали недавно.
XVR
Цитата(UniBomb @ Mar 18 2009, 16:50) *
А начсёт процедур - что за процедура UI?
User Interface - взаимодействие с пользователем
UniBomb
Спасибо всем за ответы, я подчерпнул для себя много нового smile.gif

Палыч, если бы все эти функции могли бы выполняться одна за одной, то я бы таймер вообще не использовал. Так например между вызовами функциями BeginTempConvers(); и GetTempValue(); должно пройти по крайней мере полсекунды. Это особенность микросхемы-термометра. Плюс я ещё оставляю между вызовами функций много времени для того, чтобы свободного процессорного времени было много. Это делается для того, чтобы при использовании USART было бы побольше времени для выполнения функций, реализующих какой-либо протокол. Плюс ко всему в последнем проекте используеться микросхема памяти, запись данных в которую происходит очень много времени.

_Pasha, ммм... а что такое "атомарный"? smile.gif

DpInRock, насчёт алгоритма понятно. Просто задачи я решаю в основном однотипные, которые сводятся к элементарной линейноти и очерёдности...

haker_fox, ваши слова заставили меня задуматься... Чуствую в моих программах грядут большие изменения

XVR ясно)))
ukpyr
я делаю примерно как в http://electronix.ru/forum/index.php?showt...st&p=564433 .

один из таймеров заряжаю на 200..2000 Гц, в его прерывании обновляю динамическую индикацию из буфера в памяти, сканирую клавиатуру (для экономии выводов она обычно завязана на линии индикации) в буфер клавиатуры. плюс есть несколько счетчиков для получения опорных частот 100, 10, 1Гц.
в основном цикле на частоте 100 Гц производится опрос и обработка клавиатуры, индикация обновляется с частотой 10 Гц, и т.д.
Палыч
Цитата(UniBomb @ Mar 19 2009, 13:26) *
если бы все эти функции могли бы выполняться одна за одной, то я бы таймер вообще не использовал. Так например между вызовами функциями BeginTempConvers(); и GetTempValue(); должно пройти по крайней мере полсекунды...
.... Плюс ко всему в последнем проекте используеться микросхема памяти, запись данных в которую происходит очень много времени.
В простейшем случае можно поставить и задержку biggrin.gif . Правда на полсекунды - это, наверное, очень "круто" (даже для "простых" задач). Грамотнее использовать автомат состояний и использовать таймер как источник сигналов для него. Автоматы обсуждаются тут - рядом.
Цитата(UniBomb @ Mar 19 2009, 13:26) *
... Это делается для того, чтобы при использовании USART было бы побольше времени для выполнения функций, реализующих какой-либо протокол.
Обычно ввод/вывод по USART делают буферированным, и по прерываниям от USART извлекают/помещают из/в буфера очередной байт, при этом сама аппаратура USART (собственно ввод/вывод) работает в параллель с программой и, возможно, приёмник впараллель с передатчиком...
UniBomb
Палыч, у меня с усартом почти также))) Почти потому, что приём данных парсинг данных, подготовка ответных данных и инициация передачи у меня происходит в прерывании по приёму данных. Я так сделал по нескольким причинам - во-первых я могу в этом случае отсчтывать временные интервалы между пакетами данных, во-вторых я не позволяю обновляться внутренним данным обновляться во время обмена, да и много ещё почему... Передача же данных буферизирована и не задерживает программу.

В общем спасибо всем за ответы, я получил ответ на свой вопрос)))
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.