"Алгоритмы и блок-схемы - это что такое?" Помощь новичкам.

Lipt0n
Offline
Зарегистрирован: 04.11.2013

Здравствуйте!

В этой теме я расскажу как блок-схемы помогают облегчить жизнь. Она ориентированна на людей, которые малознакомы с программированием (вобще неважно каким).

Как известно, весь код выполняется последовательно. То есть абсолютно ВСЕ операции которые выполняются внутри наших микроконтроллеров, выполняются один за другим растягиваясь на такты - это те странные МегаГерцы которые можно увидеть в спецификациях плат arduino (в даташите можно посмотреть сколько тактов занимают простейшие операции).

В чём секрет программистов, которые пишут такое большое колличество строк кода и могут разобраться в том как ЭТО работает?

Первым чему учат программистов - "что такое алгоритм", это и есть их "секрет".

Вырезка из Вики -

Алгори́тм — набор инструкций, описывающих порядок действий исполнителя для достижения результата решения задачи за конечное число действий .

 

Возьмем для примера всем известный Blink.

digitalWrite(led, HIGH);  
  delay(1000);              
  digitalWrite(led, LOW);    
  delay(1000); 

 

Его алгоритм будет выглядеть так:

1.Включаем светодиод. 
2.Ждем секунду. 
3.Выключаем светодиод. 
4.Ждем секунду. 
5.Возвращаемся к началу.

*Обратите внимание, что алгоритм может быть разной "глубины" - вместо того чтобы написать "Устанавливаем значение ноги в высокое состояние", я написал "Включаем светодиод". Также "delay(1000)" я заменил на "Ждем секунду.".

С помощью блок-схем мы можем представить графическое изображние алгоритма.

Графическое изображение воспринимается куда лучше чем текстовое, не так ли?:)

*Теория алгоритмов говорит, что в каждой программе обязательно должны быть начало и конец, который в данном примере отсутствует(в данном случае переход на этот блок будет осуществлен в случае отключения питания).

Блок-схемы бывают следующих типов:

  -  блок, который определяет начало и конец программы.Имеет вид начало/конец.

- блок, в котором выполняются какие-то действия.Имеет вид c=a+b;

- блок, в котором происходит проверка условия.Имеет вид a>b?

- блок, который подразумевает вызов функции.

- блок ввода/вывода информации. Может иметь вид вроде 'a = '+ a;

- используется для циклов while и for. (но как по мне пользоваться блоком условия проще).

  Для соединения блоков используются стрелочки.

 

Возьмем пример посложнее!

На этот раз мы опишем программу, которая после нажатия кнопки будет выводить в последовательный порт значения массива.

int massiv[8]={1,2,3,4,5,6,7,8};  
int button = 13;

И сам код, который находится в loop(){}

 

if(digitalRead(button)==1){
  for(int i=0;i<8;i++)
    {
    Serial.println(massiv[i]);
    }
}  

 

В текстовом алгоритме это будет выглядеть так: 

1. Кнопка нажата?
     Нет - вернуться к началу.
     Да - переходим к следующему пункту. 
2. i=0; 
3. i меньше чем 8 ?
     Нет - вернуться у началу.
     Да - переходим к следующему пункту. 
4. Вывод в последовательный порт значения элемента массива с индексом i - (massiv[i]).
5. Прибавляем к i единицу - i++. 
6. Возвращаемся к началу условия (пункт 3).

В графическом соответственно так:

Ну вроде всё должно работать как задумано.

Но что же это? Мы нажимаем на кнопку, но вместо того чтобы один раз вывести данные массива, они выводятся несколько раз.

С точки зрения логики алгоритма всё работает так как и задумано. Вся соль в том, что для Вас - это одно нажатие, а вот с точки зрения МК - это может оказаться целой вечностью(множество циклов).

Он видит это так:

10ms - О, нажата кнопка, пора приниматься за дело. 
30ms - Всё сделано как надо, можно возвращаться обратно. 
30ms - О, кнопка снова нажата! Опять есть чем заняться! 
50ms - Всё сделано! Вернусь-ка я обратно! 
50ms - Опять? Ну ладно...

Для того чтобы этого не происходило, нужно ввести переменную, которая будет показывать предыдущее состояние кнопки. Добавим её в наш код -

int massiv[8]={1,2,3,4,5,6,7,8};  
int button = 13;
boolean pressed = false;

 

if(digitalRead(button)==1 and pressed==false){
  pressed=true;
  for(int i=0;i<8;i++)
    {
    Serial.println(massiv[i]);
    }
}      
if(digitalRead(button)==0){
pressed=false;
}

Теперь МК будет видеть всё так:

10ms - О, кнопочка! Пойду поработаю. 
30ms - Готово! Вернусь я в начало. 
30ms - Опять кнопочка нажата! Пора.... А неееет, кнопка до сих пор нажата, эту работу я уже выполнил, можно отдохнуть.

 

С помощью такой вот штуковины становится намного легче найти свою ошибку или даже точно уяснить для себя что должно делать устройство.

PS. для рисования блок-схем я использую онлайн сервис https://www.gliffy.com/ .

 

Клапауций
Offline
Зарегистрирован: 10.02.2013

всё красиво на словах, но каким образом контроллеру становится понятно, что  нужно делать 5.Возвращаемся к началу. ?

т.е. каким образом луп определяет, где у него начало, а где конец, и есть ли в этом смысл? - если по сути имеем кольцо команд. тогда и диаграмму алгоритма рисовать в виде кольца с обозначением точки старта.

Lipt0n
Offline
Зарегистрирован: 04.11.2013

В графиках у меня ошибки с точки зрения теории алгоритмов. В ней сказано, что обязательно должны быть начало и конец. 

Конец же будет выглядеть настолько глупо, что я его не писал("глубина" алгоритма не та). 

Есть питание? 
    да - продолжать работу 
    нет - конец

В данном случае начало начинается сразу после setup(){}. Ведь по сути алгоритм начинается с подачи питания на МК, а затем уже идет setup и loop. Loop представляет собой на уровне команд тот же цикл while - то есть если код для выполнения дальше отсутствует МК прыгает обратно к строке начала цикла.

Насколько я помню то в ассемблере while(1) выгдядит как команда безусловного перехода jump. Которая перескакивает на нужную строку. Получается круг, но как бы не круг.

И по сути указанные алгоритмы loop и есть те же круги.  

А вот если придать ему вид кольца, то оно будет занимать куда больше места и будет восприниматься куда сложнее.

UPD. Если Вы имели ввиду то, что следовало написать "Возвращаемся к пункту 1" - то действительно так будет правильно. 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Если это учебный материал, то зачем учить плохому?

if(digitalRead(button)==1 and pressed==false)

Вообще то digitalRead возвращает HIGH или LOW и не важно, что

#define HIGH 0x1
#define LOW  0x0

Давайте просто посмотрим в исходник и убедимся:

int digitalRead(uint8_t pin)
{
	uint8_t timer = digitalPinToTimer(pin);
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);

	if (port == NOT_A_PIN) return LOW;

	// If the pin that support PWM output, we need to turn it off
	// before getting a digital reading.
	if (timer != NOT_ON_TIMER) turnOffPWM(timer);

	if (*portInputRegister(port) & bit) return HIGH;
	return LOW;
}

Если это помощь новичкам, то давайте будем корректно писать примеры.

IMHO, разумеется