Машинка на ДУ управлении своими руками. Цифровая последовательность. Передатчик (AVR)

На этом уроке мы с Вами разберём программную реализацию интерфейса передатчика с цифровой, байт ориентированной последовательности на микроконтроллере AVR.

Что значит, бит или байт ориентированная?? В случае бит ориентированной последовательности мы передаём последовательность бит, которые не объединяются в байты или слова (несколько байт), а передают независимую, обособленную информацию. Если нам необходимо передать набор чисел, например, то на помощь приходит байт ориентированная последовательность, биты которой объединяются в байты, слова и т.п.

Определимся с протоколом. Как положено вначале передаём стартовые байты (вместо стартовой паузы). Затем данные: мощность двигателя — один байт, направление движения — один бит, направления поворота — два бита. Данные защитим битом чётности (паритета) — один бит. Добавим контрольную сумму — один байт. В окончании кадра передадим стоповые байты.

Рассмотрение программы начнём с заголовочного файла передатчика transmitter.h.

Настроим передатчик. Укажем стартовую и стоповую последовательности…

 #define START_SEQUENCE "ABC" //Стартовые байты #define STOP_SEQUENCE "&&"   //Стоповые байты

Зададим скорость передачи данных…

 #define PULSE_DURATION() _delay_ms(1); //Частота передачи 1000 бод

Объявим перечисляемый тип данных…

 enum logic_state {HIGH, LOW}; //Логическое состояние

Объявим необходимые переменные…

 unsigned char powerEngine,   //Мощность двигателя          commandBuffer,      //Буффер комманд          charBuffer,         //Буффер передатчика строки             checkSum;           //Байт контрольной суммы  enum {EVEN, ODD} parityBit = ODD; //Флаг паритета

Для передачи данных понадобятся три функции: передача бита, байта и строки, объявим их…

 void transmitBit(enum logic_state txBuffer); //Передача бита... void transmitByte(unsigned char txBuffer); //Передача байта... void transmitString(unsigned char txBuffer[]); //Передача строки...

Настройки портов ввода-вывода и АЦП из файла initHard.c остаются прежними.

Теперь рассмотрим основную программу.

Мы не используем, процедуру инициализации убираем. Порты и АЦП проинициализируем…

 initPORTs();    //Инициализация портов initADC();      //Настройка АЦП 

Программа работает следующим образом: опрашиваем клавиши поворота и заднего хода…

 //Опрос клавиши направление движения. Загрузить полученные данные в commandBuffer if (!(PIND & (1 << PD0))) commandBuffer |= (1 << 0); else commandBuffer &= ~(1 << 0);  //Опрос клавиш направления поворота. Загрузить полученные данные в commandBuffer if (!(PIND & (1 << PD1))) commandBuffer |= (1 << 1); else commandBuffer &= ~(1 << 1); //Влево if (!(PIND & (1 << PD2))) commandBuffer |= (1 << 2); else commandBuffer &= ~(1 << 2); //Вправо

Формируем байты данных, для того, чтобы сохраняем мгновенное значение мощности в буфер…

 //Загружаем мгновенное значение мощности      powerEngine = ADCH; //...или остановить АЦП

Вычисляем бит паритета…

 //Проверяем на чётность передаваемые данные if ((powerEngine & (1 << 0)) || (commandBuffer & (1 << 0))) parityBit = ODD; else parityBit = EVEN;

Вычисляем контрольную сумму…

 //Вычисляем контрольную сумму checkSum = 0x41 + 0x42 + 0x43 + powerEngine + commandBuffer + 0x26 + 0x26;

Передаём стартовые байты…

 //Передача стартовых байт      transmitString(START_SEQUENCE);

Передаём данные…

 //Передача байта мощности      transmitByte(powerEngine);  //Передача байта commandBuffer      transmitByte(commandBuffer); 

Передаём бит чётности…

 //Передача бита чётности      if (parityBit == EVEN) transmitBit(HIGH); else transmitBit(LOW); 

Передаём байт контрольной суммы…

 //Передача байта контрольной суммы      transmitByte(checkSum); 

Передаём стоповые байты…

 //Передача стоповых байт      transmitString(STOP_SEQUENCE);

АЦП опрашивает ручку газа (производит измерения) в автоматическом режиме.

Разберёмся с работой функций передачи данныхФункция transmitBit. Если при вызове аргумент функции равен HIGH, то конструкция…

 if (txBuffer == HIGH) PORTB |= (1 << PB0); //Формируем фронт

…формирует фронт логической единицы. Далее следует пауза PULSE_DURATION()…

 _delay_ms(1); //Частота передачи 1000 бод

…которая обеспечивает скорость передачи в 1000 бод. Следом формируется безусловный срез…

 PORTB &= ~(1 << PB0);  //Формируем срез

В случае, если аргумент функции равен LOW, функция формирует паузу в 1ms.

Функция transmitByte, в качестве аргумента получает один байт, 8 бит которого передаются в цикле…

 for (unsigned char i = 0; i < 8; i++) {...

Проверяем «нулевой» бит, в зависимости от его значения формируем фронт или срез…

 //Формируем фронт/срез              if (txBuffer & (1 << 0)) PORTB |= (1 << PB0); else PORTB &= ~(1 << PB0);

…формируем паузу…

 PULSE_DURATION(); //Длительность логического состояния

Сдвигаем переменную txBuffer вправо на один разряд. Эта процедура повторяется 8 раз, для передачи 8 бит.

Функция transmitString, в качестве аргумента получает строку символов. Перед началом передачи вычисляется количество символов в строке strlen(txBuffer), затем в цикле…

 for (unsigned char i = 0; i < strlen(txBuffer); i++) {...

…загружаем первый передаваемый символ в буфер передатчика…

 charBuffer = txBuffer[i];

…происходит передача 8 байт текущего байта…

 for (unsigned char i = 0; i < 8; i++) { //Формируем фронт/срез       if (charBuffer & (1 << 0)) PORTB |= (1 << PB0); else PORTB &= ~(1 << PB0);            PULSE_DURATION(); //Длительность логического состояния      charBuffer >>=  1; //Сдвиг вправо на одну позицию }

Эта процедура повторяется для каждого передаваемого символа.

На сегодня всё! До новых встреч!!

ПРОЕКТ С ИСХОДНЫМ КОДОМ И СИМУЛЯЦИЯ В PROTEUS во вложении.