Учимся работать со светофором

В этом уроке:

  • Устанавливаем светофор на трассу
  • Распознаём сигналы светофора
  • Учим машинку случайно выбирать направление движения

Установка светофора на трассу

Мы уже преодолели большую часть трассы, но впереди у нас остался самый сложный и запутанный маршрут. Итак, первым на пути попадается перекрёсток, на котором установлен светофор с дополнительными секциями. Соберите светофор и настройте его на наш тип перекрёстка — Х-образный, если ещё этого не делали. После этого возвращайтесь к этому уроку. 

Учим машинку ездить по сигналам светофора

Сейчас нам очень помогут знания из урока о поворотах на перекрёстке. В скетче того урока было сделано одно допущение: мы постоянно были «в ожидании» перекрёстка, поскольку не знали, в какой момент он появится. Флаг flg_CrossWait постоянно равен true для возможности поворачивать на перекрёстках. Однако, теперь мы чётко знаем, в какой момент будет перекрёсток, ведь перед ним установлен светофор. Поэтому мы можем наконец установить флаг по умолчанию false, как и было запланировано. Менять его на true будем при получении сигнала от светофора.

Давайте посмотрим на фрагмент скетча для светофора и разберёмся, как он работает.

case MODUL_TLIGHT:               // ПРИНЯТЫ ДАННЫЕ ОТ МОДУЛЯ "СВЕТОФОР":       
  if( flg_TLIGHT == false ){     // Если данные от светофора еще не получались
    flg_TLIGHT =  true;          // Фиксируем факт получения данных от светофора
      
    //  Выбираем направление поворота:                      
    if( !ir.track_L &&  ir.track_F && !ir.track_R ){ val_Turn=0;                 } // Перекрёсток светофора имеет дорогу только прямо (F), выбираем движение прямо 0
    if( !ir.track_L &&  ir.track_F &&  ir.track_R ){ val_Turn=random( 0,2);      } // Перекрёсток светофора имеет дорогу прямо (F) и направо (R) , выбираем движение прямо 0 или направо 1
    if(  ir.track_L &&  ir.track_F && !ir.track_R ){ val_Turn=random(-1,1);      } // Перекрёсток светофора имеет дорогу прямо (F) и налево (L), выбираем движение налево -1 или прямо 0
    if(  ir.track_L &&  ir.track_F &&  ir.track_R ){ val_Turn=random(-1,2);      } // Перекрёсток светофора имеет дорогу в любом направлении (LFR), выбираем движение налево -1, прямо 0, или направо 1
    if(  ir.track_L && !ir.track_F &&  ir.track_R ){ val_Turn=random( 0,2)? -1:1;} // Перекрёсток светофора имеет дорогу налево и направо (LR), выбираем движение налево -1 или направо 1
    flg_CrossWait = val_Turn ? true : false;                                       // Ждём начала перекрёстка, если требуется поворачивать (если val_Turn != 0)
         
    //  Включаем поворотники:  
    if( val_Turn==-1 ){ bum.setTurnSignal(BUM_TURN_LEFT ); }                            
    if( val_Turn== 0 ){ bum.setTurnSignal(BUM_TURN_OFF  ); }
    if( val_Turn== 1 ){ bum.setTurnSignal(BUM_TURN_RIGHT); }
  }                                                        
      
  //   Снижаем скорость или останавливаемся на светофоре:       
  if( flg_CrossWait ){ speed = low_Speed; }             // Если ожидается перекрёсток, то снижаем скорость перед ним
  else               { speed = mid_Speed; }             // Иначе выбираем обычную скорость
  if( val_Turn== 0 && ir.forvard==0 ){ speed = 0; }     // Если выбрано движение прямо  и оно запрещено светофором, то останавливаемся (снижаем скорость до 0%)
  if( val_Turn==-1 && ir.left   ==0 ){ speed = 0; }     // Если выбрано движение влево  и оно запрещено светофором, то останавливаемся (снижаем скорость до 0%)
  if( val_Turn== 1 && ir.right  ==0 ){ speed = 0; }     // Если выбрано движение вправо и оно запрещено светофором, то останавливаемся (снижаем скорость до 0%)
break;     

В уже знакомую конструкцию switch-case мы добавили код для светофора. В строках 111-115 происходит выбор направления поворота случайным образом. Подробнее об этом мы говорили в уроке,  где учились объезжать препятствия. Для корректной работы этой функции в void setup() мы добавили строку:

randomSeed(analogRead(A2));   // Выбираем начальную позицию для генерации случайных чисел

Также мы перенесли в кейс светофора фрагмент кода с управлением поворотниками (118-121 строки), ведь они необходимы только во время работы с перекрёстком. И будет логично поместить в одно место код для выполнения одной задачи (движения на перекрёстке).

Далее устанавливаем скорость проезда перекрёстка (125,126 строки). Если ожидается перекрёсток, то снижаем скорость до 40 км/ч, иначе движемся со скоростью 60 км/ч.

Строки 127-129 запрещают движение автомобиля в заданном направлении (которое мы ранее определили случайно) при запрещающем сигнале светофора.

Также немного корректируем фрагмент кода с ожиданием перекрёстка:

//   ПРОВЕРКА НАЛИЧИЯ ПЕРЕКРЁСТКОВ:                                       
if( bum.getCross(3,1000) ){              // Если под бампером обнаружен перекрёсток (указываем толщину линии трассы и время на преодоление перекрёстка в мс) ...
  flg_TLIGHT = false;                    // Сбрасываем флаг наличия данных от светофора
  disp.clrScr();                         // Чистим экран светодиодной матрицы
  if( flg_CrossWait ){                   // Если ожидается появление перекрёстка ...
    flg_CrossWait = false;               // Сбрасываем флаг ожидания    перекрёстка
    flg_CrossFind = true;                // Устанавливаем флаг обнаружения перекрёстка
  }                                                               
}

Добавляем сбрасывание флага светофора (87 строка) при въезде на перекрёсток (86 строка), а также переносим сюда функцию очищения дисплея (88 строка).

Анимация схемы проезда перекрёстка на индикаторе

Мы добавили функцию анимации проезда перекрёстка. Оставляем её без объяснения работы, только с комментариями к коду. При желании Вы можете самостоятельно разобраться с тем, как это работает. 

//   Функция создания анимации движения на перекрёстке светофора:
void fnc_CreateImage(bool l, bool f, bool r, int8_t t){        // Параметры: l - наличие дороги влево, f - прямо, r - вправо, t - выбранное направление (L=-1 F=0 R=+1)
//   Рисуем дорогу к перекрёстку:
     Image_TLIGHT[0]=0x18; Image_TLIGHT[1]=0x18; Image_TLIGHT[2]=0x18; Image_TLIGHT[3]=0x18; Image_TLIGHT[4]=0x18; Image_TLIGHT[5]=0; Image_TLIGHT[6]=0; Image_TLIGHT[7]=0;
//   Добавляем дороги от перекрёстка:
     if( f  ){ Image_TLIGHT[5]|=0x18; Image_TLIGHT[6]|=0x18; Image_TLIGHT[7]|=0x18; }
     if( l  ){ Image_TLIGHT[3]|=0x1F; Image_TLIGHT[4]|=0x1F; } // Дорога влево
     if( r  ){ Image_TLIGHT[3]|=0xF8; Image_TLIGHT[4]|=0xF8; } // Дорога вправо
//   Добавляем точку движения:                                 
     static uint8_t i=0, j=0;                                  // Определяем переменные: i - позиция точки, f - флаг задержки
     if( millis()%50 < 25 ){                                   
         if( j==0 ){ j=1; i++; if(i>8){i=0;} }                 // Увеличиваем позицию i
     }else{ j=0; }                                             // Сбрасываем задержку
     if(i<=3){ Image_TLIGHT[i]&=0xEF; }                        // Точка на дороге к перекрёстку
     else{ if(t==0){ if(i<8){Image_TLIGHT[i]&=0xEF;        } } // Точка на дороге от перекрёстка прямо
           if(t <0){ if(i<9){Image_TLIGHT[4]&=~(1<<(8-i)); } } // Точка на дороге от перекрёстка влево
           if(t >0){ if(i<7){Image_TLIGHT[3]&=~(1<<(i+1)); } } // Точка на дороге от перекрёстка вправо
     }                                                         
}            

Запуск данной функции происходит во время получения данных от светофора.

//   АНИМАЦИЯ ДВИЖЕНИЯ НА СВЕТОФОРЕ: 
if( flg_TLIGHT ){                                                 // Если от светофора получены данные
  fnc_CreateImage(ir.track_L, ir.track_F, ir.track_R, val_Turn);  // Генерируем изображение перекрёстка светофора
  disp.drawImage(Image_TLIGHT);                                   // Выводим изображение перекрёстка светофора
} 

Готовый скетч урока

#include <Wire.h>                         // Подключаем библиотеку для работы с аппаратной шиной I2C
#include <iarduino_I2C_Motor.h>           // Подключаем библиотеку для работы с мотором  I2C-flash
#include <iarduino_I2C_Bumper.h>          // Подключаем библиотеку для работы с бампером I2C-flash
#include <iarduino_I2C_IR.h>              // Подключаем библиотеку для работы с Trema модулями: ИК-приёмник/передатчик I2C-flash
#include <iarduino_I2C_Matrix_8x8.h>      // Подключаем библиотеку для работы с LED матрицей 8x8 I2C-flash
                                         
iarduino_I2C_Motor mot_R (0x0A);          // Объявляем объект mot_R для правого мотора, указав адрес модуля на шине I2C
iarduino_I2C_Motor mot_L (0x0B);          // Объявляем объект mot_L для правого мотора, указав адрес модуля на шине I2C
iarduino_I2C_Bumper bum (0x0C);           // Объявляем объект bum  для работы с бампером I2C-flash, указав адрес модуля на шине I2C
iarduino_I2C_Matrix_8x8 disp (0x0D);      // Объявляем объект disp для работы с светодиодной матрицей I2C-flash, указав адрес модуля на шине I2C
iarduino_I2C_IR ir(0x09);                 // Объявляем объект ir для работы с функциями и методами библиотеки iarduino_I2C_IR, указывая адрес модуля на шине I2C
 
                                            
const float min_Speed = 20;               // Минимальная  скорость движения в км/ч. Используется при движении вблизи школ, а также на опасных участках дороги
const float low_Speed = 40;               // Низкая скорость движения в км/ч. Используется при движении по опасным участкам дороги или при соответствующем знаке
const float mid_Speed = 60;               // Средняя скорость движения в км/ч. Используется при движении по дорогам
const float max_Speed = 90;               // Максимальная скорость движения в км/ч. Используется при движении по скоростным дорогам
 
float speed = mid_Speed;                  // Указываем, что изначально скорость равна средней
int8_t  val_Turn      = 0;                // Выбранное направление движения на перекрёстке: 0 прямо, -1 влево, +1 вправо
bool    flg_TLIGHT = false;               // Флаг получения данных от светофора
bool    flg_CrossWait = false ;           // Флаг ожидания перекрёстка (машина «увидела» светофор или знак, сообщающий о наличии перекрёстка)
bool    flg_CrossFind = false;            // Флаг обнаружения перекрёстка (бампер заехал на перекрёсток)
bool    flg_Turn = false;                 // Флаг необходимости разворота          
bool    flg_25 = false;                   // Флаг повторной фиксации знака 2.5 «Движение без остановки запрещено»
uint32_t timeSign;                        // Время для отсчёта времени перед повторной фиксацией знака
byte Image_TLIGHT[8];                     // Генерируется функцией fnc_CreateImage()

//   1.12.1 «Опасные повороты»            //  Изображение знака «Опасные повороты»
byte Image_1_12_1[8] = { 0b00000000,      //
                         0b00000010,      //       #
                         0b00100010,      //   #   #
                         0b01010010,      //  # #  #
                         0b01001010,      //  #  # #
                         0b01000100,      //  #   #
                         0b01000000,      //  #
                         0b00000000 };    //
 
//   3.31 «Конец зоны ограничений»        //
byte Image_3_31[8] =   { 0b00111100,      //   ####
                         0b01000010,      //  #    #
                         0b10000101,      // #    # #
                         0b10001001,      // #   #  #
                         0b10010001,      // #  #   #
                         0b10100001,      // # #    #
                         0b01000010,      //  #    #
                         0b00111100 };    //   ####
void setup() {
  mot_R.begin();                          // Инициируем работу с левым  мотором I2C-flash
  mot_L.begin();                          // Инициируем работу с правым мотором I2C-flash
  bum.begin();                            // Инициируем работу с бампером I2C-flash
  disp.begin();                           // Инициируем работу с светодиодной матрицей I2C-flash
  disp.codingDetect("п");                 // Выполняем автоопределение кодировки скетча
  mot_R.setDirection(true);               // Указываем правому мотору, что его вращение должно быть прямым (по часовой стрелке при положительных скоростях)
  mot_L.setDirection(false);              // Указываем левому мотору, что его вращение должно быть обратным (против часовой стрелки при положительных скоростях)
  ir.begin();                             // Инициируем работу с ИК-приёмником/передатчиком I2C-flash
  ir.setProtocol(IR_IARDUINO);            // Указываем протокол для приёма/передачи данных по ИК-каналу
  randomSeed(analogRead(A2));             // Выбираем начальную позицию для генерации случайных чисел
}
 
void loop() {
  
//   АНИМАЦИЯ ДВИЖЕНИЯ НА СВЕТОФОРЕ: 
  if( flg_TLIGHT ){                                                 // Если от светофора получены данные
    fnc_CreateImage(ir.track_L, ir.track_F, ir.track_R, val_Turn);  // Генерируем изображение перекрёстка светофора
    disp.drawImage(Image_TLIGHT);                                   // Выводим изображение перекрёстка светофора
  }  
//    РАЗВОРОТ:
//if (bum.getCross(3, 1000)) flg_Turn = true;// Если обнаружен перекрёсток, устанавливаем флаг разворота
if( flg_Turn ){                            // 1 ФАЗА. Если под бампером обнаружен перекрёсток (указываем толщину линии трассы и время на преодоление перекрёстка в мс) ...
                                           // Под бампером обнаружен перекрёсток:
  flg_Turn = false;                        // Сбрасываем флаг разворота
  mot_R.setSpeed( convertSpeed(min_Speed), MOT_PWM );    // Устанавливаем скорость правого мотора в %
  mot_L.setSpeed(-convertSpeed(min_Speed), MOT_PWM );    // Устанавливаем скорость левого мотора в %
                                           //         Ждём завершения манёвра:
  while( bum.getLineSum() > 0 ) {;}        // 2 ФАЗА. Ждём выхода линии за пределы бампера. Цикл выполняется, пока под бампером есть линия
  while( bum.getLineSum() == 0 ){;}        // 3 ФАЗА. Ждём появления линии под бампером. Цикл выполняется, пока под бампером нет  линии
                                           //         Останавливаемся:
  mot_R.setStop();                         //         Останавливаем правое колесо
  mot_L.setStop();                         //         Останавливаем левое колесо
  val_Turn  = 0;                           // 4 ФАЗА. Выбираем движение прямо
  speed = mid_Speed;                       //         Снимаем наложенные ранее ограничения скорости
}
 
//   ПРОВЕРКА НАЛИЧИЯ ПЕРЕКРЁСТКОВ:                                       
  if( bum.getCross(3,1000) ){              // Если под бампером обнаружен перекрёсток (указываем толщину линии трассы и время на преодоление перекрёстка в мс) ...                            
    flg_TLIGHT = false;                    // Сбрасываем флаг наличия данных от светофора
    disp.clrScr();                         // Чистим экран светодиодной матрицы
    if( flg_CrossWait ){                   // Если ожидается появление перекрёстка ...
      flg_CrossWait = false;               // Сбрасываем флаг ожидания    перекрёстка
      flg_CrossFind = true;                // Устанавливаем флаг обнаружения перекрёстка
    }                                                               
  }
else{                                      // Если под бампером обычная линия ...          
     if (flg_CrossFind) {                  // Если ранее был обнаружен перекрёсток 
       flg_CrossFind = false;              // Сбрасываем флаг обнаружения перекрёстка
       val_Turn = 0;                       // Выбираем движение прямо
       speed = mid_Speed;                  // Снимаем наложенные ранее ограничения скорости
       bum.setTurnSignal(BUM_TURN_OFF);    // Отключаем поворотники
     }                                                               
  }

//   ПРОВЕРКА НАЛИЧИЯ ПРИНЯТЫХ ИК-ДАННЫХ:                                  
  if( ir.check(true) ){                    //  Если принят пакет данных от знака
    switch (ir.device) {
      case MODUL_TLIGHT:                   //  ПРИНЯТЫ ДАННЫЕ ОТ МОДУЛЯ СВЕТОФОР:       
        if( flg_TLIGHT == false ){         //  Если данные от светофора еще не получались
          flg_TLIGHT =  true;              //  Фиксируем факт получения данных от светофора
      
          //  Выбираем направление поворота:                      
          if( !ir.track_L &&  ir.track_F && !ir.track_R ){ val_Turn=0;                 } // Перекрёсток светофора имеет дорогу только прямо (F), выбираем движение прямо 0
          if( !ir.track_L &&  ir.track_F &&  ir.track_R ){ val_Turn=random( 0,2);      } // Перекрёсток светофора имеет дорогу прямо (F) и направо (R), выбираем движение прямо 0 или направо 1
          if(  ir.track_L &&  ir.track_F && !ir.track_R ){ val_Turn=random(-1,1);      } // Перекрёсток светофора имеет дорогу прямо (F) и налево (L), выбираем движение налево -1 или прямо 0
          if(  ir.track_L &&  ir.track_F &&  ir.track_R ){ val_Turn=random(-1,2);      } // Перекрёсток светофора имеет дорогу в любом направлении (LFR), выбираем движение налево -1, прямо 0, или направо 1
          if(  ir.track_L && !ir.track_F &&  ir.track_R ){ val_Turn=random( 0,2)? -1:1;} // Перекрёсток светофора имеет дорогу налево и направо (LR), выбираем движение налево -1 или направо 1
          flg_CrossWait = val_Turn ? true : false;                                       //Ждём начала перекрёстка, если требуется поворачивать (если val_Turn != 0)
         
          //  Включаем поворотники:  
          if( val_Turn==-1 ){ bum.setTurnSignal(BUM_TURN_LEFT ); }                            
          if( val_Turn== 0 ){ bum.setTurnSignal(BUM_TURN_OFF  ); }
          if( val_Turn== 1 ){ bum.setTurnSignal(BUM_TURN_RIGHT); }
        }                                                        
      
        //   Снижаем скорость или останавливаемся на светофоре:       
        if( flg_CrossWait ){ speed = low_Speed; }             // Если ожидается перекрёсток, то снижаем скорость перед ним
        else               { speed = mid_Speed; }             // Иначе выбираем обычную скорость
        if( val_Turn== 0 && ir.forvard==0 ){ speed = 0; }     // Если выбрано движение прямо и оно запрещено светофором, то останавливаемся (снижаем скорость до 0%)
        if( val_Turn==-1 && ir.left   ==0 ){ speed = 0; }     // Если выбрано движение влево и оно запрещено светофором, то останавливаемся (снижаем скорость до 0%)
        if( val_Turn== 1 && ir.right  ==0 ){ speed = 0; }     // Если выбрано движение вправо и оно запрещено светофором, то останавливаемся (снижаем скорость до 0%)
      break;     
        
      case MODUL_SIGN:                                         // Данные отправлены дорожным знаком
        if(!strcmp(ir.sign_str, "1.12.1")){                    // Если номер знака 1.12.1 - «Опасные повороты»          
          speed = min_Speed;                                   // Ограничиваем скорость до ближайшего перекрёстка или нового знака
          disp.drawImage(Image_1_12_1);                        // Выводим изображение знака
        }
        if( !strncmp( ir.sign_str, "3.24", 4) ){               // Обнаружен знак ПДД 3.24 «Ограничение максимальной скорости»:
          disp.print(ir.sign[2]);                              // Выводим цифру скорости
          if( ir.sign[2]< 3 ){ speed = min_Speed; }else        // ir.sign[2] < 3 значит, на знаке написано меньше «30»
          if( ir.sign[2]< 5 ){ speed = low_Speed; }else        // ir.sign[2] < 5 значит, на знаке написано меньше «50»
          if( ir.sign[2]< 7 ){ speed = mid_Speed; }else        // ir.sign[2] < 7 значит, на знаке написано меньше «70»
          if( ir.sign[2]!=0 ){ speed = max_Speed; }            // ir.sign[2] != 0 значит, на знаке написано «90»
        } 
        if( !strcmp( ir.sign_str, "1.23" ) ){                  // Eсли номер знака 1.23 - «Дети»
          disp.scrollPos(0);                                   // Переходим к началу бегущей строки
          disp.print("Дети");                                  // Загружаем текст для бегущей строки
          disp.autoScroll(245);                                // Выводим загруженный текст однократно со скоростью 245
          speed = min_Speed;                                   // Ограничиваем скорость до ближайшего перекрёстка или нового знака
        }
        if( !strcmp( ir.sign_str, "3.31" ) ){                  // Если обнаружен знак ПДД 3.31 «Конец зоны ограничений»
          disp.drawImage(Image_3_31);                          // Выводим изображение знака
          speed = mid_Speed;                                   // Снимаем наложенные ранее ограничения скорости
        }
        if( !strcmp( ir.sign_str, "2.5" )){                    // Если номер знака 2.5 - «Движение без остановки запрещено»
          if (!flg_25) {                                       // Если не установлен флаг знака
            timeSign = millis();                               // Сбрасываем значение переменной времени обнаружения знака
            flg_25 = true;                                     // Устанавливаем флаг знака
          }
        }
      break;
         
      case MODUL_CAR:                                          // Данные отправлены другим автомобилем
                                                               // Пока тут пусто, с другими машинками не взаимодействуем
      break;
    }     
  }
  if(flg_25 && timeSign + 3000 < millis()){                    // Если флаг установлен, и с момента последней фиксации прошло больше 3 сек
    flg_25 = false;                                            // Сбрасываем флаг
    disp.setTimeIdleFirst(100);                                // Указываем бегущей строке задерживаться на первом символе в течении 100 мс (допускаются значения от 0 до 2550 мс)
    disp.setTimeIdleLast(100);                                 // Указываем бегущей строке задерживаться на последнем символе в течении 100 мс (допускаются значения от 0 до 2550 мс)
    disp.scrollPos(0);                                         // Переходим к началу бегущей строки
    disp.print("STOP");                                        // Загружаем текст для бегущей строки
    disp.autoScroll(245);                                      // Выводим загруженный текст однократно со скоростью 245
    mot_R.setSpeed( 0, MOT_PWM );                              // Выключаем правый мотор
    mot_L.setSpeed( 0, MOT_PWM );                              // Выключаем левый мотор
    delay(2000);                                               // Задержка 2 сек (остановка)    
  }
 
//   ОПРЕДЕЛЯЕМ ОШИБКУ ЦЕНТРИРОВАНИЯ ЛИНИИ ПОД БАМПЕРОМ:                   
//   Если ожидается перекрёсток: машина должна ехать по центру линии, немного сместившись в сторону поворота
     float bum_Error;                                                      // Объявляем переменную: ошибка для П-регулятора
     if( flg_CrossWait ){ bum_Error = bum.getErrPID()+val_Turn;   }else    // Получаем ошибку центрирования линии, смещённую на 1 датчик в сторону ожидаемого поворота «val_Turn»
//   Если бампер заехал на перекрёсток:                                    // Машина должна ехать по краю линии, выполняя поворот.
     if( flg_CrossFind ){ bum_Error = bum.getSidePID(val_Turn)*3; }else    // Получаем ошибку нахождения на краю линии, со стороны поворота «val_Turn». Умножаем ошибку — это увеличит резкость поворота (актуально для тонких линий трассы)
//   Если не ждём перекрёсток и не находимся на нём:                       // Машина должна ехать по центру линии
                        { bum_Error = bum.getErrPID();            }        // Получаем ошибку центрирования линии
                        
  float kP = 3 + 0.125*(convertSpeed(speed)-20);        // Коэффициент П-регулятора
  float P = bum_Error * kP;                             // Получаем значение от П-регулятора
  mot_R.setSpeed(convertSpeed(speed) - P, MOT_PWM);     // Устанавливаем скорость правого мотора 
  mot_L.setSpeed(convertSpeed(speed) + P, MOT_PWM);     // Устанавливаем скорость левого мотора 
}
 
float convertSpeed(float speedLevel){                   // Функция преобразования скорости из км/ч в % ШИМ
  if (speedLevel == 0) return 0;                        // Если скорость равна 0, возвращаем 0
  else if (speedLevel < 20) return 20;                  // Если скорость меньше минимальной, устанавливаем минимальное заполнение ШИМ
  else if (speedLevel > 90) return 60;                  // Если скорость больше максимальной, устанавливаем максимальное заполнение ШИМ
  else return map(speedLevel, 20, 90, 20, 60);          // Преобразование диапазона остальных скоростей
}

//   Функция создания анимации движения на перекрёстке светофора:
void fnc_CreateImage(bool l, bool f, bool r, int8_t t){        // Параметры: l - наличие дороги влево, f - прямо, r - вправо, t - выбранное направление (L=-1 F=0 R=+1)
//   Рисуем дорогу к перекрёстку:
     Image_TLIGHT[0]=0x18; Image_TLIGHT[1]=0x18; Image_TLIGHT[2]=0x18; Image_TLIGHT[3]=0x18; Image_TLIGHT[4]=0x18; Image_TLIGHT[5]=0; Image_TLIGHT[6]=0; Image_TLIGHT[7]=0;
//   Добавляем дороги от перекрёстка:
     if( f  ){ Image_TLIGHT[5]|=0x18; Image_TLIGHT[6]|=0x18; Image_TLIGHT[7]|=0x18; }
     if( l  ){ Image_TLIGHT[3]|=0x1F; Image_TLIGHT[4]|=0x1F; } // Дорога влево
     if( r  ){ Image_TLIGHT[3]|=0xF8; Image_TLIGHT[4]|=0xF8; } // Дорога вправо
//   Добавляем точку движения:                                 
     static uint8_t i=0, j=0;                                  // Определяем переменные: i - позиция точки, f - флаг задержки
     if( millis()%50 < 25 ){                                   
         if( j==0 ){ j=1; i++; if(i>8){i=0;} }                 // Увеличиваем позицию i
     }else{ j=0; }                                             // Сбрасываем задержку
     if(i<=3){ Image_TLIGHT[i]&=0xEF; }                        // Точка на дороге к перекрёстку
     else{ if(t==0){ if(i<8){Image_TLIGHT[i]&=0xEF;        } } // Точка на дороге от перекрёстка прямо
           if(t <0){ if(i<9){Image_TLIGHT[4]&=~(1<<(8-i)); } } // Точка на дороге от перекрёстка влево
           if(t >0){ if(i<7){Image_TLIGHT[3]&=~(1<<(i+1)); } } // Точка на дороге от перекрёстка вправо
     }                                                         
}
Поздравляю с изучением данного урока!
Следующий урок:
№14. Работа со шлагбаумом. Ж/Д переезд.
приступить к изучению

Продукт в магазине

Комплект знаков для базового курса по "Роботраффику" для образования (продолжение ROBORACE)

В магазин

Обсуждение

Гарантии и возврат. Используя сайт, Вы соглашаетесь с условиями.