Устанавливаем на трассу светофор

В этом уроке:

  • Устанавливаем светофор
  • Принимаем и распознаем сигналы светофора
  • Проезжаем перекрёсток под нужные сигналы светофора

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

bool 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( 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 мы добавили код для светофора. В строках 96-100 происходит выбор направления поворота случайным образом, в зависимости от конфигурации перекрёстка. Подробнее о генераторе случайных чисел мы говорили в уроке,  где учились объезжать препятствия. Для корректной работы этой функции не забываем добавить в void setup() строку:

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

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

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

Также добавляем сбрасывание флага светофора (68 строка) при въезде на перекрёсток (67 строка).

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

Поскольку теперь машинка будет включать поворотники для проезда перекрёстка, добавляем функцию их выключения после того, как перекрёсток был пройдён:

bum.setTurnSignal(BUM_TURN_OFF);    // Отключаем поворотники

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

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

//   Функция создания анимации движения на перекрёстке светофора:
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
                                         
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 для работы с LED матрицей 8x8, указывая её адрес на шине 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;                  // Указываем, что изначально скорость равна средней (60 км/ч)
int8_t  val_Turn      = 0;                // Выбранное направление движения на перекрёстке: 0 прямо, -1 влево, +1 вправо
bool    flg_CrossWait = false ;           // Флаг ожидания перекрёстка (машина «увидела» светофор или знак, сообщающий о наличии перекрёстка)
bool    flg_CrossFind = false;            // Флаг обнаружения перекрёстка (бампер заехал на перекрёсток)
bool    flg_Turn = false;                 // Флаг необходимости разворота                  
bool    flg_25 = false;                   // Флаг знака 2.5 «Движение без остановки запрещено»
uint32_t timeSign;                        // Время для задержки срабатывания знака 2.5
bool    flg_TLIGHT = false;               // Флаг получения данных от светофора
byte Image_TLIGHT[8];                     // Генерируется функцией fnc_CreateImage()

void setup() {
  mot_R.begin();                          // Инициируем работу с левым  мотором I2C-flash
  mot_L.begin();                          // Инициируем работу с правым мотором I2C-flash
  bum.begin();                            // Инициируем работу с бампером I2C-flash
  mot_R.setDirection(true);               // Указываем правому мотору, что его вращение должно быть прямым (по часовой стрелке при положительных скоростях)
  mot_L.setDirection(false);              // Указываем левому мотору, что его вращение должно быть обратным (против часовой стрелки при положительных скоростях)
  ir.begin();                             // Инициируем работу с ИК-приёмником/передатчиком I2C-flash
  ir.setProtocol(IR_IARDUINO);            // Указываем протокол для приёма/передачи данных по ИК-каналу
  disp.begin();                           // Инициируем работу с LED матрицей 8x8
  disp.codingDetect("п");                 // Автоматическое определение кодировки (если выводим русский язык)
  disp.setTimeIdleFirst(100);             // Указываем бегущей строке задерживаться на первом символе в течение 100 мс (допускаются значения от 0 до 2550 мс)
  disp.setTimeIdleLast(100);              // Указываем бегущей строке задерживаться на последнем символе в течение 100 мс (допускаются значения от 0 до 2550 мс)
  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( 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( 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( 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( !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, "2.5" )){                       // Если номер знака 2.5 - «Движение без остановки запрещено»
          if (!flg_25) {                                          // Если не установлен флаг знака (знак зафиксирован первый раз)
            timeSign = millis();                                  // Сбрасываем значение переменной времени обнаружения знака
            flg_25 = true;                                        // Устанавливаем флаг знака
          }  
        }
      break;

      case MODUL_CAR:                                             // Данные отправлены другим автомобилем
        // Здесь пока пусто, с другими машинками не взаимодействуем
      break;
    }   
  }
 
  // Срабатывание по времени знака «Движение без остановки запрещено»
  if(flg_25 && timeSign + 2000 < millis()){                       // Если флаг установлен и с момента последней фиксации прошло больше 2 сек
    flg_25 = false;                                               // Сбрасываем флаг
    disp.print("STOP");                                           // Загружаем текст для бегущей строки
    disp.autoScroll(245);                                         // Выводим загруженный текст однократно со скоростью 245
    mot_R.setSpeed( 0, MOT_PWM );                                 // Выключаем правый мотор
    mot_L.setSpeed( 0, MOT_PWM );                                 // Выключаем левый мотор
    delay(1000);
    while(bum.getErrPID() > -4) {                                 // Поворот вправо
      mot_R.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
      mot_L.setSpeed(convertSpeed(min_Speed), MOT_PWM);
    }
    while(bum.getErrPID() < 4) {                                  // Поворот влево
      mot_R.setSpeed(convertSpeed(min_Speed), MOT_PWM);
      mot_L.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
    }
    while(bum.getErrPID() > 0) {                                  // Поворот вправо, возвращаемся к центру
      mot_R.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
      mot_L.setSpeed(convertSpeed(min_Speed), MOT_PWM);
    }    
   }
 
//  ОПРЕДЕЛЯЕМ ОШИБКУ ЦЕНТРИРОВАНИЯ ЛИНИИ ПОД БАМПЕРОМ:                   
// Если ожидается перекрёсток: машина должна ехать по центру линии, немного сместившись в сторону поворота
  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)); } } // Точка на дороге от перекрёстка вправо
     }                                                         
}
Поздравляю с изучением данного урока!
Следующий урок:
№12. Устанавливаем шлагбаум. Ж/Д переезд.
приступить к изучению

Обсуждение

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