Главная » Радиоуправление
Призовой фонд
на октябрь 2020 г.
1. 1500 руб
Сайт Паяльник
2. Тестер компонентов MG328
Сайт Паяльник
3. 150 руб.
От пользователей


Губка для очистки жала паяльника

CxemCAR на .NET Micro Framework - Bluetooth управление машинкой с Android

Первая, вводная часть статьи проекта CxemCAR с описанием ПО и исходниками для Android находится здесь. В данной статье я хотел бы описать вариант проекта для контроллеров с .NET Micro Framework. По сравнению с проектом на STM32, изменений мало, за исключением замены контроллера и программной части для него.
 
В качестве контроллера можно использовать Netduino, платы GHI Electronics и др. Я использовал FEZ Panda II:
 
Плата FEZ Panda II
Питание DC двигателей осуществляется от Li-Po аккумуляторов 3.7В 1100 мА. Контроллер питается от отдельного аккумулятора 3.7В (хотя требуется 5В, но прекрасно работает и от 3.7В). Питание Bluetooth модуля берется с платы FEZ.

Фото элементов питания:

Фото элементов питания

Схема подключения выглядит следующим образом:

Схема подключения

Плату FEZ Panda II к 4WD шасси прикрепил при помощи 2-х стороннего скотча:

uprav52-4.jpg

Далее, все было собрано и подключено. Итог на фото:

uprav52-5.jpg

Исходник для .NET Micro Framework:

using System;
using System.IO.Ports;
using System.Threading;
using System.Text;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

namespace CxemCAR
{
    public class Program
    {
        public const char cmdL = 'L';       // команда UART для левого двигателя
        public const char cmdR = 'R';       // команда UART для правого двигателя
        public const char cmdH = 'H';       // команда UART для доп. канала 1 (к примеру сигнал Horn)
        public const char cmdF = 'F';       // команда UART для работы с EEPROM памятью МК для хранения настроек
        public const char cmdr = 'r';       // команда UART для работы с EEPROM памятью МК для хранения настроек (чтение)
        public const char cmdw = 'w';       // команда UART для работы с EEPROM памятью МК для хранения настроек (запись)

        //public const int t_TOut = 2500;     // кол-во миллисекунд, через которое машинка останавливается при потери связи
        static int sw_autoOFF;
        static int autoOFF = 2500;
        static byte[] storage = new byte[InternalFlashStorage.Size];                                // переменная для хранения значений FLASH памяти МК

        static OutputPort MotorL_d = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di4, false);           // направление вращения двигателя 1
        static OutputPort MotorR_d = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di7, false);           // направление вращения двигателя 2
        static OutputPort Channel1 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di8, false);           // доп. канал 1
        static PWM MotorL = new PWM((PWM.Pin)FEZ_Pin.PWM.Di5);                                      // ШИМ вывод для управления двигателем 1 (левый)
        static PWM MotorR = new PWM((PWM.Pin)FEZ_Pin.PWM.Di6);                                      // ШИМ вывод для управления двигателем 2 (правый)
        static SerialPort UART1 = new SerialPort("COM1", 9600);                                     // новый объект UART1 (порт COM1)
        static Timer timerTO;                                                                       // таймер
       
        public static void Main()
        {
            byte[] L_Data = new byte[4];        // строковый массив для данных мотора L
            byte L_index = 0;                   // индекс массива
            byte[] R_Data = new byte[4];        // строковый массив для данных мотора R
            byte R_index = 0;                   // индекс массива
            byte[] H_Data = new byte[1];        // строковый массив для доп. канала
            byte H_index = 0;                   // индекс массива
            byte[] F_Data = new byte[8];        // строковый массив данных для работы с EEPROM
            byte F_index = 0; 
            char command = ' ';                 // команда: передача координат R, L, H, F или конец строки

            int i_tmp_L = 0;
            int i_tmp_R = 0;
            int i_tmp_H = 0;

            byte[] incomingByte = new byte[1];  // байт с UART
          
            UART1.Open();
            UART1.Flush();

            timerTO = new Timer(new TimerCallback(TimeOut), null, autoOFF, autoOFF);  // инициализация таймера потери связи
            timer_init();                                                             // инициализируем программный таймер

            while (true)
            {
                int read_count = UART1.Read(incomingByte, 0, 1);        // считываем данные
                if (read_count > 0)                                     // пришли данные?
                {
                    if (incomingByte[0] == cmdL)                        // если пришли данные для мотора L
                    {
                        command = cmdL;                                 // текущая команда
                        Array.Clear(L_Data, 0, L_Data.Length);          // очистка массива
                        L_index = 0;                                    // сброс индекса массива
                    }
                    else if (incomingByte[0] == cmdR)                   // если пришли данные для мотора R
                    {
                        command = cmdR;                                 // текущая команда
                        Array.Clear(R_Data, 0, R_Data.Length);          // очистка массива
                        R_index = 0;                                    // сброс индекса массива
                    }
                    else if (incomingByte[0] == cmdH)                   // если пришли данные для доп. канала 1
                    {
                        command = cmdH;                                 // текущая команда
                        Array.Clear(H_Data, 0, H_Data.Length);          // очистка массива
                        H_index = 0;                                    // сброс индекса массива
                    }
                    else if (incomingByte[0] == cmdF)                   // если пришли данные для доп. канала 1
                    {
                        command = cmdF;                                 // текущая команда
                        Array.Clear(F_Data, 0, F_Data.Length);          // очистка массива
                        F_index = 0;                                    // сброс индекса массива
                    }
                    else if (incomingByte[0] == '\r') command = 'e';    // конец строки
                    else if (incomingByte[0] == '\t') command = 't';    // конец строки для команд работы с памятью


                    if (command == cmdL && incomingByte[0] != cmdL)
                    {
                        if (ValidData(incomingByte[0]))
                        {
                            L_Data[L_index] = incomingByte[0];              // сохраняем каждый принятый байт в массив
                            if (L_index < (L_Data.Length - 1)) L_index++;   // увеличиваем текущий индекс массива
                        }
                    }
                    else if (command == cmdR && incomingByte[0] != cmdR)
                    {
                        if (ValidData(incomingByte[0]))
                        {
                            R_Data[R_index] = incomingByte[0];
                            if (R_index < (R_Data.Length - 1)) R_index++;
                        }
                    }
                    else if (command == cmdH && incomingByte[0] != cmdH)
                    {
                        if (ValidData(incomingByte[0]))
                        {
                            H_Data[H_index] = incomingByte[0];
                            if (H_index < (H_Data.Length - 1)) H_index++;
                        }
                    }
                    else if (command == cmdF && incomingByte[0] != cmdF)
                    {
                        F_Data[F_index] = incomingByte[0];
                        if (F_index < (F_Data.Length - 1)) F_index++;
                     }
                    else if (command == 'e')                                // если приняли конец строки
                    {
                        timerTO.Dispose();                                  // останавливаем таймер потери связи
                        string tmp_L = new string(System.Text.UTF8Encoding.UTF8.GetChars(L_Data));      // формируем строку из массива
                        string tmp_R = new string(System.Text.UTF8Encoding.UTF8.GetChars(R_Data));
                        string tmp_H = new string(System.Text.UTF8Encoding.UTF8.GetChars(H_Data));

                        try
                        {
                            if (tmp_L != null) i_tmp_L = int.Parse(tmp_L);                              // и пытаемся преобразовать в int
                            if (tmp_R != null) i_tmp_R = int.Parse(tmp_R);
                            if (tmp_H != null) i_tmp_H = int.Parse(tmp_H);
                        }
                        catch { 
                            Debug.Print("Error: convert String to Integer"); 
                        }


                        if (i_tmp_L > 100) i_tmp_L = 100;
                        else if (i_tmp_L < -100) i_tmp_L = -100;
                        if (i_tmp_R > 100) i_tmp_R = 100;
                        else if (i_tmp_R < -100) i_tmp_R = -100;

                        Control4WD(i_tmp_L, i_tmp_R, i_tmp_H);
                        timerTO.Change(autoOFF, autoOFF);                                               // таймер считает сначала
                    }
                    else if (command == 't')                                                            // если приняли конец строки для работы с памятью
                    {
                        Flash_Op(F_Data[0], F_Data[1], F_Data[2], F_Data[3], F_Data[4]);
                    }
                }
            }
        }

        static void Flash_Op(byte FCMD, byte z1, byte z2, byte z3, byte z4)
        {
            if (FCMD == cmdr && sw_autoOFF != 255)                              // если команда чтения EEPROM данных 
            {
                byte[] buffer = Encoding.UTF8.GetBytes("FData:");               // подготавливаем байтовый массив для вывода в UART
                UART1.Write(buffer, 0, buffer.Length);                          // запись данных в UART
                byte[] buffer2 = new byte[4] { storage[0], storage[1], storage[2], storage[3] };
                UART1.Write(buffer2, 0, buffer2.Length);
                byte[] buffer3 = Encoding.UTF8.GetBytes("\r\n");
                UART1.Write(buffer3, 0, buffer3.Length);
            }
            else if (FCMD == cmdw)                                              // если команда записи EEPROM данных
            {
                byte[] varToSave = new byte[InternalFlashStorage.Size];
                varToSave[0] = z1;
                varToSave[1] = z2;
                varToSave[2] = z3;
                varToSave[3] = z4;
                InternalFlashStorage.Write(varToSave);                          // запись данных в FLASH память МК
                timer_init();		                                            // переинициализируем таймер
                byte[] buffer2 = Encoding.UTF8.GetBytes("FWOK\r\n");            // подготавливаем байтовый массив для вывода в UART
                UART1.Write(buffer2, 0, buffer2.Length);	                    // посылаем сообщение, что данные успешно записаны
            }
        }

        static void timer_init()
        {
            InternalFlashStorage.Read(storage);                                 // чтение данных с FLASH памяти
            sw_autoOFF = storage[0];
            if(sw_autoOFF == '1'){                                              // если таймер останова включен
                byte[] var_Data= new byte[3];
                var_Data[0] = storage[1];
                var_Data[1] = storage[2];
                var_Data[2] = storage[3];
                string tmp_autoOFF = new string(System.Text.UTF8Encoding.UTF8.GetChars(var_Data));
                autoOFF = int.Parse(tmp_autoOFF)*100;
                timerTO.Change(autoOFF, autoOFF);                               // изменяем параметры таймера
            }
            else if(sw_autoOFF == '0'){
                timerTO.Dispose();                                              // выключаем таймер
            } 

            Debug.Print("Timer Init" + autoOFF.ToString());
        }

        static void TimeOut(object o)
        {
            //Debug.Print(DateTime.Now.ToString());
            Control4WD(0, 0, 0);                                                // при таймауте останавливаем машинку
        }

        public static void Control4WD(int mLeft, int mRight, int Horn)
        {
            bool directionL, directionR;                                        // направление вращение для L298N
            int valueL, valueR;                                                 // значение ШИМ M1, M2 (0-100)

            if (mLeft > 0)
            {
                valueL = mLeft;
                directionL = false;
            }
            else if (mLeft < 0)
            {
                valueL = 100 - System.Math.Abs(mLeft);
                directionL = true;
            }
            else
            {
                directionL = false;
                valueL = 0;
            }

            if (mRight > 0)
            {
                valueR = mRight;
                directionR = false;
            }
            else if (mRight < 0)
            {
                valueR = 100 - System.Math.Abs(mRight);
                directionR = true;
            }
            else
            {
                directionR = false;
                valueR = 0;
            }

            if (Horn == 1)
            {
                Channel1.Write(true);
            }
            else Channel1.Write(false);

            //Debug.Print("L:" + valueL.ToString() + ", R:" + valueR.ToString());
            
            MotorL.Set(30000, (byte)(valueL));
            MotorR.Set(30000, (byte)(valueR));

            MotorL_d.Write(directionL);
            MotorR_d.Write(directionR);
        }

        public static bool ValidData(byte chIncom)                  // проверка поступившего символа на принадлежность к "0..9" или "-"
        {
            if ((chIncom >= 0x30 && chIncom <= 0x39) || chIncom == 0x2D) return true;
            else return false;
        }
    }
}

Сама программа под FEZ не очень сложная - в цикле считываем данные с UART и формируем соответствующие массивы. Как только получаем символ окончания передачи (\r или \t), то данные преобразовываются и передаются в соответствующие функции Control4WD() или Flash_Op(). В функции Control4WD() происходит вычисление направления, а также небольшие расчеты и затем управляющие сигналы выводятся на соответствующие пины контроллера.

Проект на GitHub

Прикрепленные файлы:

Теги:

Опубликована: 20.02.2013 Изменена: 20.07.2013 0 0
Я собрал 0 1
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 5 Проголосовало: 1 чел.

Комментарии (0) | Я собрал (0) | Подписаться

Статью еще никто не комментировал. Вы можете стать первым.
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется сила тока?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Квадрокоптер Syma X11
Квадрокоптер Syma X11
Конструктор: DDS генератор сигналов LC-измеритель LC100-A
вверх