Мой прожектикъ

Kitsok

Привет всем!

Силы подходят к концу, поэтому дабы взбодрить себя, решил тэкскзть выдать на суд общественности:

Вся конструкция целиком. Станина - клееная сосна 40мм

Направляющие X (25 мм) крепятся саморезами. Пришлось зенковать, недорасчет на этапе проектирования. Вообще, таких промашек было несколько 😉

Всё на болтах, это ось Z c морды. Пропил в шпинделедержалке был сделан потом, конструкция выглядит убого, но шпиндель держит.

А это оно-же, но сзаду:

Основной вывод после сборки “железной” части - при проектировании надо МНОГО ДУМАТЬ о том, как ЭТО будет собираться

Запрессовка стальных направляющих в дюралевую несущую конструкцию не потребовала ни жидкого азота, ни сухого льда. Вошла как миленькая при помощи фена 😉

Поскольку на пурелоджиковом контроллере было всего 4 входа, да и те работали через пень-колоду, пришлось наговнякать по-быстрому modbus-контроллер. Тут тоже “сын ошибок трудных” был постижен 😉 😉, но результат вцелом устраивает:

Чуть не забыл электроящик 😉

Аж три блока питания - ну что поделаешь, зато все развязано по питанию аж два раза 😉

От идеи (где-то июль прошлого года) до реализации - считайте сами 😉

Осталось разобрать все это, отвезти на дачу в домик, подключить-запустить и напиться от пережитого за время сборки, покупки комплектующих и борьбы с темными силами электричества 😉

Конструкция выглядит солидно, весу в ней - около 30 кг. Меня по оси X катает легко 😉 Как оно режет - пока не знаю, дома запускать не хочу, буду ждать до дачи.

DmitryS

Оч интересно. НО уважаемый отчет неполный😁, что из чего и почем😁😁😁
А конкретней направляющие, винты, движки, корпуса осей.😒

Kitsok

По ценам.

Электроника (вся: контроллер + 4 движка + 4 муфты) в пуре - 10 тыщ.
Направляющие 16мм + направляющие 25мм + подшипниковые блоки под 25мм (разрезанные) - в пуре, не помню точно, вроде около 3 тысяч.
ШВП + гайки + обработка концов + доставка - 500 УЕ с доставкой (можно было бы и дешевле, но ступил. Надо было в Китае все это заказывать, а заказал в Тайване).
Опоры 16мм (8 штук) - 40 баксов, доставка - лично 😉 Можно было опоры не по 5, а по 3.5 бакса, но тогда доставка под 70 УЕ.
Дюраль - материал + изготовление деталей - 11 тыс рублей.
Крепеж - около тысячи рублей весь.
Самопиленные вещи - модбас, проставки для двигателей.
Доска - полторы тысячи рублей.
Подшпники для опор ШВП - 16 штук по 40 рублей (по нынешним ценам, я у друзей половину бесплатно забрал 😉)

Общий вывод: Нужно все готовые детали (направляющие, подшипники, винты, крепеж, моторы,…) заказывать в Китае. Даже с доставкой и честной растаможкой оно будет минимум в 3 раза дешевле, чем покупать в Москве.

Ходы: Z:80мм, Y: 340, X: 540

куций

Меня по оси X катает легко 😉

Попытался представить😂😂😂

Kitsok
куций:

Попытался представить😂😂😂

Очень, в самом деле, страшно: стоять приходится на цыпочках, и эта дура сама собой едет 😉

olkogr

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

CINN
Kitsok:

Поскольку на пурелоджиковом контроллере было всего 4 входа, да и те работали через пень-колоду, пришлось наговнякать по-быстрому modbus-контроллер. Тут тоже “сын ошибок трудных” был постижен 😉 😉, но результат вцелом устраивает:

Можно подробнее о пурелоджиковом контроллере? Что именно не так?
И, извините за дремучесть, что такое- modbus-контроллер?😃

znak

поделитемь ссылкой на швп в тиайване и катае если не трудно
очень интересно посмотреть на их цены

кстати, мне нравится ваша простота конструкции

Kitsok

Отвечаю по порядку.

по поводу крепления направляющих - согласен, узел одноразовый. Но я если и задумаю менять что-то, то это будет в новой конструкции. Там - да, сделаю клипсу.
Станина эта - тоже решение “временно-постоянное”. Т.е. если меня не устроит жесткость-вибропоглощающая способность этой станины, то буду лить из эпоксидки с наполнителем. К сожалению, для конструирования станины из металла нет ни навыков, ни инструмента.

Про контроллер. Не так - как-то странно работают входы, точнее, то работают, то не работают. Выходы работают тип-топ. Да, кроме этого, входы сделаны не как “сухие контакты”, а как входы оптопар (диоды), поэтому нужен еще один источник питания.

Модбас-контроллер - в общем-то в моей реализации это Mega16 (которая была под рукой) с 8 входами и 8 выходами. Выходы и выходы относительно меги не отвязаны, вместо этого 2-мя оптопарами отвязан MAX232. Со стороны компутера задействуется КОМ-порт, в Mach3 прекрасно работает совместно с “мозгами”.

Насчет ссылки на ШВП в тайване - я где-то уже давал тут ссылку. Вкрадце - идите на alibaba.com, регьтесь там, ищите ШВП, и дальше - личное общение с продавцом. Разница в цене между Китаем и Тайванем может составлять до 3-х раз. Мне обошлось вот в это:

1605 or 1604 nut: USD42/pcs[/COLOR]

1605 or 1604 shaft
350mm: USD13.5/pcs
540mm: USD20.5/pcs
810mm: USD31/pcs

Machining cost for 1 end: USD15   Do you need end-machining for every "end"?  Then, the total cost for end -machining will be USD15*8=USD120
Total goods cost: 120+42*4+13.5+20.5+31*2=USD384; product lead time: 10 days
Delivery cost by EMS: USD115; delivery 1 week after shipment.
Total: USD384+115=USD499  100% TT in advance

Платилось через WeternUnion. Лавка - вот эта www.lexem.com.tw но там все на неясном мне диалекте 😉 😉, поэтому чертежи мне присылались в почту.

fleshget

Почему крепеж линейных подшипников по Х только на двух болтах? Предусмотрено ж 4… так было бы жоще…
А как направляющие крепятся в опорах? Как-то стопорных болтов не видно… ни разреза…

Ой, не доглядел… на четырех… только по мойму все-таки жоще было бы непосредственно линейные подш. крепить к боковинам, без “переходной” пластины.

olkogr
fleshget:

Ой, не доглядел… на четырех… только по мойму все-таки жоще было бы непосредственно линейные подш. крепить к боковинам, без “переходной” пластины. google_ad_section_end

А опоры направляющих???

v2003

Модбас-контроллер - в общем-то в моей реализации это Mega16 (которая была под рукой) с 8 входами и 8 выходами. Выходы и выходы относительно меги не отвязаны, вместо этого 2-мя оптопарами отвязан MAX232. Со стороны компутера задействуется КОМ-порт, в Mach3 прекрасно работает совместно с “мозгами”.

Про Modbus можно поподробнее, или коммерция?

Kitsok

Если я правильно понял вопрос, то опоры направляющих Х - 8 саморезов на штуку.

Модбас - легко. Софт - freemodbus, плюс плата с косяками 😉 Если интересно, могу выложить “модификацию” софта (на самом деле, там все очень просто и прозрачно) и плату.

v2003
Kitsok:

Если я правильно понял вопрос, то опоры направляющих Х - 8 саморезов на штуку.

Модбас - легко. Софт - freemodbus, плюс плата с косяками 😉 Если интересно, могу выложить “модификацию” софта (на самом деле, там все очень просто и прозрачно) и плату.

Конечно интересно, да если можно со своими комментами или пояснениями, я в принципе так и думал, но для меня freemodbus темный лес.

CINN
Kitsok:

Про контроллер. Не так - как-то странно работают входы, точнее, то работают, то не работают.

Вы же вроде в Москве? Не пробовали связаться на сей счёт с Purelogic-ами?

Baha
CINN:

Вы же вроде в Москве? Не пробовали связаться на сей счёт с Purelogic-ами?

Если у вас Пурложик, то же проблемы будут!

v2003
CINN:

Вы же вроде в Москве? Не пробовали связаться на сей счёт с Purelogic-ами?

Все из-за того что в опторазвязке оптопары медленные походу.

CINN
Baha:

Если у вас Пурложик, то же проблемы будут!

Можно подробнее?
У меня электроника от Пурелогиков, пока занимаюсь механикой электронику не подключал. Хотелось бы знать все мнения. Негатива по поводу Пурелогиков в Сети не встречал…

DSP1

У меня PLC002 - работает хорошо .

Kitsok

Модбас.

test.c из demo/avr:

/*
 * FreeModbus Libary: AVR Demo Application
 * Copyright (C) 2006 Christian Walter <>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: demo.c,v 1.7 2006/06/15 15:38:02 wolti Exp $
 */

/* ----------------------- AVR includes -------------------------------------*/
#include "avr/io.h"
#include "avr/interrupt.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

#include "delay.h"

/* ----------------------- Defines ------------------------------------------*/
#define REG_INPUT_START 1000
#define REG_INPUT_NREGS 4
#define REG_COILS_START     1100
#define REG_COILS_SIZE      8
#define REG_DISC_START    1200
#define REG_DISC_SIZE    8
#define REG_HOLDING_START       1300
#define REG_HOLDING_NREGS       8
/* ----------------------- Static variables ---------------------------------*/
static USHORT   usRegInputStart = REG_INPUT_START;
static USHORT   usRegInputBuf[REG_INPUT_NREGS];
static UCHAR      ucRegCoilsBuf[REG_COILS_SIZE / 8];
static UCHAR     ucRegDiscBuf[REG_DISC_SIZE / 8 ];
static USHORT   usRegHoldingStart = REG_HOLDING_START;
static USHORT   usRegHoldingBuf[REG_HOLDING_NREGS];
static CHAR     cEncoderTimer=0;
static CHAR     cDirCur,cDirPrev;
static UCHAR     ucEncoderCur=0;
static UCHAR     ucEncoderPrev=0;
static CHAR     cDecoded;
static CHAR     cDecodedPrev;


/* Gray-code convertion table */
const unsigned char GrayCode[16]=
 {
  0,2,1,0,
  1,0,0,2,
  2,0,0,1,
  0,1,2,0
 };


#ifndef outb
    #define    outb(addr, data)    addr = (data)
#endif
#ifndef inb
    #define    inb(addr)            (addr)
#endif
#ifndef outw
    #define    outw(addr, data)    addr = (data)
#endif
#ifndef inw
    #define    inw(addr)            (addr)
#endif
#ifndef BV
    #define BV(bit)            (1<<(bit))
#endif
#ifndef cbi
    #define cbi(reg,bit)    reg &= ~(BV(bit))
#endif
#ifndef sbi
    #define sbi(reg,bit)    reg |= (BV(bit))
#endif
#ifndef cli
    #define cli()            __asm__ __volatile__ ("cli" ::)
#endif
#ifndef sei
    #define sei()            __asm__ __volatile__ ("sei" ::)
#endif

#define INPUT_DDR DDRB
#define INPUT_PIN PINB
#define INPUT_PORT OUTPUT_PORT

#define OUTPUT_DDR DDRC
#define OUTPUT_PIN PINC
#define OUTPUT_PORT PORTC

/* ----------------------- Start implementation -----------------------------*/
int
main( void )
{
    const UCHAR     ucSlaveID[] = { 0xAA, 0xBB, 0xCC };
    eMBErrorCode    eStatus;


    OUTPUT_DDR=0xFF;
    INPUT_DDR=0x00;
    OUTPUT_PORT=0xAA;
    delay_ms(1000);
    OUTPUT_PORT=0xff;
    eStatus = eMBInit( MB_RTU, 0x0A, 0, 4800, MB_PAR_NONE );
    OUTPUT_PORT=eStatus;
    delay_ms(1000);
    OUTPUT_PORT=0xff;

    eStatus = eMBSetSlaveID( 0x34, TRUE, ucSlaveID, 3 );
    OUTPUT_PORT=eStatus;
    delay_ms(1000);
    OUTPUT_PORT=0xff;
    sei(  );

    /* Enable the Modbus Protocol Stack. */
    eStatus = eMBEnable(  );
    OUTPUT_PORT=eStatus;
    delay_ms(1000);
    OUTPUT_PORT=0xff;

    /* Init encoder */
    usRegHoldingBuf[0]=0;
    // set interrupt pins to input and apply pullup resistor
    cbi(DDRD, PD2);
    sbi(PORTD, PD2);
    // set encoder direction pin for input and apply pullup resistor
    cbi(DDRD, PD4);
    sbi(PORTD, PD4);

    cDirCur=cDirPrev=0;
    usRegHoldingBuf[0]=100;

    for( ;; )
    {
        ( void )eMBPoll(  );
        OUTPUT_PORT = ucRegCoilsBuf[0];
        ucRegDiscBuf[0]=INPUT_PIN;

        /* Here we simply count the number of poll cycles. */
        usRegInputBuf[0]++;

        /* Process Encoder */
        /* Save the previouse encoder state */
        ucEncoderPrev = ucEncoderCur;
        /* Get current encoder value*/
        ucEncoderCur = ((PIND>>2) + ((PIND>>4)<<1)) & 0x03;

        /* Check if the state has changed */
        if (ucEncoderCur != ucEncoderPrev)
         {
           cDecoded = GrayCode[(ucEncoderPrev<<2) | ucEncoderCur];

           /* Check if the direction has changed */
           if (cDecoded == cDecodedPrev)
            {
             /* The direction is the same, decrease the timer */
             cEncoderTimer++;
            }
            else
            {
             /* The direction has changed, load the timer */
             cEncoderTimer=0;
             cDecodedPrev = cDecoded;
            }

           if ((cEncoderTimer == 3) && (cDecoded != 0))
            {
             /* The direction is stable for 4 periods, change the counter */
             cDecodedPrev=0;
             cEncoderTimer = 0;

             if (cDecoded == 0x1)
              usRegHoldingBuf[0]++;
             if (cDecoded == 0x02)
              usRegHoldingBuf[0]--;
            }


         }

    }
}

eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegInputStart );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}


eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
               eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    short           iNCoils = ( short )usNCoils;
    unsigned short  usBitOffset;

    /* Check if we have registers mapped at this block. */
    if( ( usAddress >= REG_COILS_START ) &&
        ( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) )
    {
        usBitOffset = ( unsigned short )( usAddress - REG_COILS_START );
        switch ( eMode )
        {
                /* Read current values and pass to protocol stack. */
            case MB_REG_READ:
                while( iNCoils > 0 )
                {
                    *pucRegBuffer++ =
                        xMBUtilGetBits( ucRegCoilsBuf, usBitOffset,
                                        ( unsigned char )( iNCoils >
                                                           8 ? 8 :
                                                           iNCoils ) );
                    iNCoils -= 8;
                    usBitOffset += 8;
                }
                break;

                /* Update current register values. */
            case MB_REG_WRITE:
                while( iNCoils > 0 )
                {
                    xMBUtilSetBits( ucRegCoilsBuf, usBitOffset,
                                    ( unsigned char )( iNCoils > 8 ? 8 : iNCoils ),
                                    *pucRegBuffer++ );
                    iNCoils -= 8;
                }
                break;
        }

    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}




eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    short           iNDiscrete = ( short )usNDiscrete;
    UCHAR * pucRegDiscBuf = ucRegDiscBuf;

    /* Check if we have registers mapped at this block. */
    if( ( usAddress >= REG_DISC_START ) &&
        ( usAddress + usNDiscrete <= REG_DISC_START + REG_DISC_SIZE ) )
    {
        while( iNDiscrete >> 3 )
        {
            *pucRegBuffer++ = *pucRegDiscBuf;
            iNDiscrete -= 8;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}



eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    if( ( usAddress >= REG_HOLDING_START ) &&
        ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegHoldingStart );
        switch ( eMode )
        {
            /* Pass current register values to the protocol stack. */
        case MB_REG_READ:
            while( usNRegs > 0 )
            {
                *pucRegBuffer++ = ( UCHAR ) ( usRegHoldingBuf[iRegIndex] >> 8 );
                *pucRegBuffer++ = ( UCHAR ) ( usRegHoldingBuf[iRegIndex] & 0xFF );
                iRegIndex++;
                usNRegs--;
            }
            break;

            /* Update current register values with new values from the
             * protocol stack. */
        case MB_REG_WRITE:
            while( usNRegs > 0 )
            {
                usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                iRegIndex++;
                usNRegs--;
            }
            break;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}

delay.h:

#ifndef _delay_h_
#define _delay_h_

#include <inttypes.h>
#include <avr/io.h>

/* delay function for microsec
   4 cpu cycles per loop + 1 cycles(?) overhead
   when a constant is passed. */
static inline void delayloop16(uint16_t count)
{
    asm volatile (  "cp  %A0,__zero_reg__ \n\t"  \
                     "cpc %B0,__zero_reg__ \n\t"  \
                     "breq L_Exit_%=       \n\t"  \
                     "L_LOOP_%=:           \n\t"  \
                     "sbiw %0,1            \n\t"  \
                     "brne L_LOOP_%=       \n\t"  \
                     "L_Exit_%=:           \n\t"  \
                     : "=w" (count)
                     : "0"  (count)
                   );
}
// delayloop16(x) eats 4 cycles per x
#define DELAY_US_CONV(us) ((uint16_t)(((((us)*1000L)/(1000000000/F_CPU))-1)/4))
#define delay_us(us)      delayloop16(DELAY_US_CONV(us))

/* delay function for millisec
  (6 cycles per x + 20(?) overhead) */
void delayloop32( uint32_t l); // not inline
#define DELAY_MS_CONV(ms) ( (uint32_t) (ms*(F_CPU/6000L)) )
#define delay_ms(ms)  delayloop32(DELAY_MS_CONV(ms))

/* mth 9/04:
   Remark uSeconds:
   Main Oscillator Clock given by F_CPU (makefile) in Hz
   one CPU-Cycle takes 1/F_CPU seconds => 1000000/F_CPU uSeconds
   so: 1 uSecond takes F_CPU/1000000 CPU-Cyles. The following code
   is inspired by the avr-libc delay_loop2 function.
   This it not "that precise" since it takes at least 4 cycles
   but should be o.k. with any parameter (even 0).
   Call function with delayloop(DELAYUS(dt [in uSeconds])).
*/

#endif

В test.c есть кусок (/* Process Encoder */) обработки энкодера, подключенного к пинам PD4 и PD6, но поразмыслив (в основном о том, где я буду в Москве брать MPG) решил в железе не реализовывать. Если кто воспользуется - буду рад 😉

Разводка лежит тут: nigde.ru/modbus3.lay
При этом кварц не используется (юзаю встроенный генератор на 8МГц), скорость - 4800 (нужно осцилоскопом поглядеть фронты и поискать более быстродействующие оптопары), но меня устраивает 😉

Изготавливалось методом фотолитографии на сухой фоторезист, получится ли изготовить ЛУТом - не знаю, думаю, что да.

avric

Kitsok,

  • насчёт модбаса - тема очень интересная. Производительности его хватает?

  • Вы как-то будете защищать винт ШВП и направляющие от попадания стружки? Насколько это вобще необходимо/желательно?

to all:

  • я пока делаю управление для станка. Изначально хотел расположить драйверы двигателей в непосредственной близости к ШД. Рядом поставить - не проблема. Сейчас много драйверов, практически не греющихся, на полевиках. Остаётся вопрос: есть ли в этом какой-то смысл.
Kitsok

avric, Производительности 100% хватает на 38400, на 4800 его хватит для аварийной остановки в случае попытки самоубийства станка (концевики на осях + E-Stop). Конечно, калибровать home на 4800 будет очень медленно.

Защищать ШВП очень и очень желательно, но я пойду другим путем - рядом со шпинделем будет пылесос 😉

Насчет выноса силовой части поближе к ШД - ни больших минусов ни плюсов не вижу, вполне имеющая право на существование схема.

v2003

Спасибо. Вопрос, как определяются выходы Dir и Step?

CINN
avric:
  • Вы как-то будете защищать винт ШВП и направляющие от попадания стружки? Насколько это вобще необходимо/желательно?

Хотя вопрос не мне, но, думаю, нелишним будет высказаться 😉
Защищать от мусора надо всё, что не относится к рабочей зоне.
Или, скажем так- по возможности всё.
ШВП и направляющие в работе всегда в слое смазки. На эту смазку имеет свойство налипать всякая пылинка. Со временем там уже становится много пыли, стружки и т.п. мусора. И так до тех пор, пока подклинывать не начнёт.
Зачем этого дожидаться? Лучше сразу защиту продумать.

Kitsok
CINN:

Зачем этого дожидаться? Лучше сразу защиту продумать.

… или мыть и смазывать после каждого сеанса 😉

Вцелом - согласен, но вменяемых защит такой схему станка мне не встречалось. Можно попробовать телескопическую спиральную защиту, но она отъест существенный рабочий ход.

v2003:

Спасибо. Вопрос, как определяются выходы Dir и Step?

Да никак 😉 Этот контроллер - для дискретных входов и выходов, а не для шаговых двигателей 😉 У меня как бы два контроллера - пурелоджик для управления моторами и мой для входов-выходов.