I2C драйвер коллекторных двигателей для вендингового автомата

p.masyukov
p.masyukov аватар
Offline
Зарегистрирован: 14.11.2015

Понадобилось мне как то набросать управление 12 коллекторными двигателями в вендинговом аппарате на спиралях (пружинах).

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

Картинки по запросу вендинговые автоматы

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

Решено - замена платы на свою, размещение контроллеров на каждом ряду и соединение так же в общую шину, но уже по I2C, для экономии и простоты управления.

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

Плата распайки на лотках выглядела так:

Ну в результате получилось что-то такое:

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

И плата вместе с платой соединения шин лотков (другие фото удалил, на этой разьемы для двигателей недопаяны)

Фото коллекторного двигателя с дотчиком холла.

Что это даст:

1) Этот драйвер можно также применить в том же аппарате, не знаю насколько это съекономит расходы, возможно наоборот.

2) Можно использовать для подобных аппаратов для выдачи нескольких товаров одновременно, но там где нет контроля выпадения товара (ИК датчиков).

Ну и код управления этим делом.

#include <SPI.h>
#include <Wire.h>

#define REG_SELECT_1  9
#define REG_SELECT_2  8

#define SLAVE_ADDRESS  0x01

String str = "";

int pos = 0;
int count = 0;
int data_counter = 0;
//int motors_run = 0;

// Массив колличества оборотов на текущий момент
int array_counter[12][2] = { {1, 0}, {2, 0}, {3, 0}, {4, 0},  {5, 0},  {6, 0}, {7, 0}, {8, 0}, {9, 0}, {10, 0}, {11, 0}, {12, 0} }; 

// массив последнего последнего оборота (время в миллисекундах)
unsigned long taimers[12] = {0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0};


byte reg_1 = 0b00000000, reg_2 = 0b00000000;

void receiveEvent(int howMany)
{
    while (Wire.available()) 
    {
        str += (char)Wire.read();
    }
    
    if (str.length() > 0)
    {
        if (str.indexOf("#") > -1)
        {
            // Заполняем байт первого и второго регистров
            edit_bytes();   


            // Заносим текущее время во все ячейки массива таймеров
            for(int i = 0; i < 12; i++) 
            {
                taimers[i] = millis();
            }

            /*
            // Отправляем байты регистрам и стартуем моторы
            
            digitalWrite(REG_SELECT_1, LOW);
            SPI.transfer(reg_1);
            digitalWrite(REG_SELECT_1, HIGH);
            delay(10);
            
            digitalWrite(REG_SELECT_2, LOW);
            SPI.transfer(reg_2);
            digitalWrite(REG_SELECT_2, HIGH);
            delay(10);

            motors_run = 1;
            */

        }
        else
        {
            data_counter++;

            if(data_counter % 2 == 1)
            {
                pos = str.toInt();
            }
            else
            {
                count = str.toInt();
                    
                // Пишем в массив для текущего ряда
                for(int i = 0; i < 12; i++)
                {
                    if(array_counter[i][0] == pos) { array_counter[i][1] = count; break;}
                }
                
                pos = 0;               
                count = 0;

                data_counter = 0;
            }
        }
    }

    str = "";
}

void setup()
{
    // Пины датчиков
    
    pinMode(0, INPUT); digitalWrite(0, HIGH);
    pinMode(1, INPUT); digitalWrite(1, HIGH);
    pinMode(2, INPUT); digitalWrite(2, HIGH);
    pinMode(3, INPUT); digitalWrite(3, HIGH);
    pinMode(4, INPUT); digitalWrite(4, HIGH);
    pinMode(5, INPUT); digitalWrite(5, HIGH);
    
    pinMode(6, INPUT); digitalWrite(6, HIGH);
    pinMode(7, INPUT); digitalWrite(7, HIGH);
    pinMode(A0, INPUT); digitalWrite(A0, HIGH);
    pinMode(A1, INPUT); digitalWrite(A1, HIGH);
    pinMode(A2, INPUT); digitalWrite(A2, HIGH);
    pinMode(A3, INPUT); digitalWrite(A3, HIGH);


    // Центровка двигателей

        // Получаем текущие состояния

            // В цикле проходим по каждому датчику
            
                // Прокручиваем 2 секунды назад
                    
                    // Если нет магнита крути на два магнита вперед
        
                    // Если же уперлись крутим на один магнит вперед

    // Теперь можно приступить к работе    
    
    Wire.begin(SLAVE_ADDRESS); 
    Wire.onReceive(receiveEvent); 

    SPI.begin();
    
    pinMode(REG_SELECT_1, OUTPUT);
    pinMode(REG_SELECT_2, OUTPUT);

    digitalWrite(REG_SELECT_1, LOW); // выбор ведомого - нашего регистра  
    SPI.transfer(0); // очищаем содержимое регистра
    digitalWrite(REG_SELECT_1, HIGH);
    delay(10);
    
    digitalWrite(REG_SELECT_2, LOW); // выбор ведомого - нашего регистра  
    SPI.transfer(0); // очищаем содержимое регистра
    digitalWrite(REG_SELECT_2, HIGH);
    delay(10);
}

void edit_bytes()
{
    
    int iBit = 2;
    
    //reg_1 |= 0x00;
    //reg_1 |= 0x00;
    
    for(int i = 0; i < 6; i++) 
    {
        if(array_counter[i][1] > 0) 
        {
            reg_1 |= (1<<iBit);        
        }
        else
        {
            reg_1 &= ~(1<<iBit);
        }
        iBit++;     
    }
    
    iBit = 2;
    
    //reg_2 |= 0x00;
    //reg_2 |= 0x00;
    
    for(int i = 6; i < 12; i++) 
    {
        if(array_counter[i][1] > 0) 
        {
            reg_2 |= (1<<iBit);            
        }
        else
        {
            reg_2 &= ~(1<<iBit);
        }
        iBit++;     
    }
        
    // Отправляем байты регистрам и стартуем моторы
            
    digitalWrite(REG_SELECT_1, LOW);
    SPI.transfer(reg_1);
    digitalWrite(REG_SELECT_1, HIGH);
    delay(10);
    
    digitalWrite(REG_SELECT_2, LOW);
    SPI.transfer(reg_2);
    digitalWrite(REG_SELECT_2, HIGH);
    delay(10);
    
}

void loop()
{
    while (1) 
    {
        int edit = 0;
        
        //if(reg_1 == 0b00000000 && reg_2 == 0b00000000)
        //{
        //    motors_run = 0;
        //}
            
        //if(motors_run == 1)
        //{
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок первого мотора
            if(digitalRead(0) == LOW)
            {            
                if(array_counter[0][1] > 0 && millis() > taimers[0] + 5000) 
                {
                    array_counter[0][1]--; 
                    edit = 1;        
                    taimers[0] = millis();
                }
            }
    
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок второго мотора
            if(digitalRead(1) == LOW)
            {            
                if(array_counter[1][1] > 0 && millis() > taimers[1] + 5000) 
                {
                    array_counter[1][1]--;
                    edit = 1;    
                    taimers[1] = millis();      
                }
            }
    
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок третьего мотора
            if(digitalRead(2) == LOW)
            {            
                if(array_counter[2][1] > 0 && millis() > taimers[2] + 5000) 
                {
                    array_counter[2][1]--;
                    edit = 1;      
                    taimers[2] = millis(); 
                }
            }
            
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок четвертого мотора
            if(digitalRead(3) == LOW)
            {            
                if(array_counter[3][1] > 0 && millis() > taimers[3] + 5000) 
                {
                    array_counter[3][1]--; 
                    edit = 1;          
                    taimers[3] = millis();
                }
            }
            
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок пятого мотора
            if(digitalRead(4) == LOW)
            {            
                if(array_counter[4][1] > 0 && millis() > taimers[4] + 5000) 
                {
                    array_counter[4][1]--;
                    edit = 1;  
                    taimers[4] = millis();        
                }
            }
            
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок шестого мотора
            if(digitalRead(5) == LOW)
            {            
                if(array_counter[5][1] > 0 && millis() > taimers[5] + 5000) 
                {
                    array_counter[5][1]--;
                    edit = 1;       
                    taimers[5] = millis();  
                }
            }
            
            //----//----//----//
            
            // следующие 6 моторов
    
            //----//----//----//
    
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок седьмого мотора
            if(digitalRead(6) == LOW)
            {            
                if(array_counter[6][1] > 0 && millis() > taimers[6] + 5000) 
                {
                    array_counter[6][1]--; 
                    edit = 1;    
                    taimers[6] = millis();     
                }
            }
            
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок восьмого мотора
            if(digitalRead(7) == LOW)
            {            
                if(array_counter[7][1] > 0 && millis() > taimers[7] + 5000) 
                {
                    array_counter[7][1]--;
                    edit = 1;     
                    taimers[7] = millis();     
                }
            }
    
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок девятого мотора
            if(digitalRead(A0) == LOW)
            {            
                if(array_counter[8][1] > 0 && millis() > taimers[8] + 5000) 
                {
                    array_counter[8][1]--;
                    edit = 1;      
                    taimers[8] = millis();  
                }
            }
    
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок десятого мотора
            if(digitalRead(A1) == LOW)
            {            
                if(array_counter[9][1] > 0 && millis() > taimers[9] + 5000) 
                {
                    array_counter[9][1]--;  
                    edit = 1;      
                    taimers[9] = millis();  
                }
            }
    
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок одинадцатого мотора
            if(digitalRead(A2) == LOW)
            {            
                if(array_counter[10][1] > 0 && millis() > taimers[10] + 5000) 
                {
                    array_counter[10][1]--;   
                    edit = 1;        
                    taimers[10] = millis();
                }
            }
    
            // Следим за состоянием пинов и массивом колличества оборотов  -- блок двенадцатого мотора
            if(digitalRead(A3) == LOW)
            {            
                if(array_counter[11][1] > 0 && millis() > taimers[11] + 5000) 
                {
                    array_counter[11][1]--;   
                    edit = 1;  
                    taimers[11] = millis();        
                }
            }
        //}
    
        if(edit == 1) 
        {
            delay(100);
            edit_bytes();
        }
    }
}

Код писал с первого раза практически, а так как работает стабильно, даже не стал его окультуривать.

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

p.masyukov
p.masyukov аватар
Offline
Зарегистрирован: 14.11.2015

Вместо основной платы всем управляет временно Ардуино нано.

Схемы и печатки по запросу.

Вот видео в работе в режиме теста (выключение двигателей по нарастающей) https://www.youtube.com/embed/o9F65UPXNuU

Если есть вопросы пишите на скайп p.masyukov

kit2005
Offline
Зарегистрирован: 06.08.2021

Добрый день, очень интересное решение. Хотел поинтересоваться, можно ли обратиться к вам за полной удаленной реализацией такого решения. Есть вендинговый аппарат с MDB, но нужно подключить его к ПК, чтобы все команды по выдаче товара приходили с компьютера, лучше по сети, но подойдут и другие решения (теоретически можно полностью отключить купюроприемник и монетоприемник) так как основное нужен управляемый модуль управления моторами.  

b707
Offline
Зарегистрирован: 26.05.2017

kit2005 пишет:

Хотел поинтересоваться, можно ли обратиться к вам...

Автор вам не ответит, он на этом сервере забанен.

Он же оставил координаты - туда и обращайтесь.

p.masyukov
p.masyukov аватар
Offline
Зарегистрирован: 14.11.2015

kit2005 пишет:

Добрый день, очень интересное решение. Хотел поинтересоваться, можно ли обратиться к вам за полной удаленной реализацией такого решения. Есть вендинговый аппарат с MDB, но нужно подключить его к ПК, чтобы все команды по выдаче товара приходили с компьютера, лучше по сети, но подойдут и другие решения (теоретически можно полностью отключить купюроприемник и монетоприемник) так как основное нужен управляемый модуль управления моторами.  

Очень интересно. Я тут крайне редко, так как работы валом.