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

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?

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

CINN
Kitsok:

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

Это через некоторое время надоест…😉

Kitsok:

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

Не обязательно. С внешних сторон можно поставить жёсткие щитки Г-образного сечения(гнутые из стали, пластика). С внутренней стороны(где крепление портала к винтам) шторка-гармошка из ПВХ(кожзама и т.п.).

olkogr

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

Kitsok

Гармошки мне больше нравятся. Тем более, что поглядев на реальную железяку (вот оно, отличие от 3Д модели 😉 ), видно, что место под гармошку есть!

dberegovoy

Интересная конструкция. Подскажите если не трудно габариты каретки Зет (длина, ширина, высота, вес). Очень интересно как он в работе, результаты уже есть?

olkogr
Kitsok:

Гармошки мне больше нравятся. Тем более, что поглядев на реальную железяку (вот оно, отличие от 3Д модели ), видно, что место под гармошку есть! google_ad_section_end

Да ну и чем же лучше?? Короб никуда не двигается и неизнашеввется, всегда есть доступ чтоб смазать трущееся детали, тем более что пылик вы всеравно ставить будете. Кроме того где взять эти гармошки и сколько они служить будут это еще тот вопрос.

Kitsok
olkogr:

Короб никуда не двигается

Тогда я не понял, куда Вы предлагаете поставить короб.

dberegovoy, хотите модель в SolidWorks? Размеров там много разных…
Согласно солиду (а он в общем довольно точно считает вес), ось Z вместе с подшипниками и гайкой Y, весит 6.9 кг.

Ширина - 130, глубина (наибольшая) 186, высота min:453.55, max 496.05.

dberegovoy
Kitsok:

модель SolidWorks?

Спасибо, от солида не откажусь, выложите в тему может еще кому пригодится.
Станок уже в работе? Есть готовые образцы изделий? Очень интересно.

olkogr
Kitsok:

Тогда я не понял, куда Вы предлагаете поставить короб

Например по Х делаем П- образную деталь изз жести надеваем ее на ходовой винт с напраляющей, сбоку делаем прорезь в которой ездит плита которя соиденяет гайку с боковинами. У вас останется щель гдето 20мм, из которой будет под давлением выходить воздух, и туда ниче не попадет.
По У тоже самое из жести закрываем все (между стойками) оставляем прорези на подшипники, по Z нуно думать там сложнее, но если станок уже готов то на готовом легче все посмотреть. Во все короба подводим воздуховодами например от пылесоса воздух, и все дело в шляпе, туда пыль никогда не попадет.

Kitsok
olkogr:

Например по Х делаем П- образную деталь изз жести надеваем ее на ходовой винт с напраляющей, сбоку делаем прорезь

Вкурил.
Зачет!!! Буду рисовать.

Станок еще не в работе, во вторник поедет в разобранном виде на дачу, где будет собираться окончательно и запускаться.

Kitsok

Я даже не знаю, какая лицензия подразумевает “отчет по прочтении” 😉, но, друзья, вы хоть свистните, все ли понятно или нет.

Сегодня обнаружил, что в текущей (той, что в архиве) модели все собираемо при соблюдении последовательности. Не хватает отверстий под концевики, но они все встают нормально, очень забавно Mach3 гоняет оську при поиске home.
Проблема на текущий момент - в ногах под станину. Как появятся - будет фотоотчет.

Kitsok

9 месяцев от идеи до первого результата. Как ребенка родил. Точнее, за это время второй сын успел родиться, вырости и уже начать орать, как потерпевший по всякому поводу 😉

Сегодня запутался в настройках Мача, плюс телефонный звонок во время reference axis, и - привет, разобрал(ся) подшипниковый узел. Не я виноват - крутящий момент 😉

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

Поздравьте меня, други, я перехожу из категории “размышлятелей” в категорию “создавших”. Это приятно.

Предварительные выводы:

  1. При проектировании надо очень очень очень много думать о том, из чего оно будет собираться. Какая у “этого” точность. Как оно ведет себя при перепадах температуры. Где это купить. Что вы купите, заказав это (!!!). Не нужно тут особенно комплексовать, но и забывать об этих факторах нельзя. Это может быть фатально для проекта.
  2. Сборка. Нужно думать о том, как вы будете собирать свой станок на этапе проектирования. Желательно… нет, необходимо!!! продумать - как будут проходить кабели, как будут крепиться концевики, как будут идти кабельгоны, и т.д. и т.п. уже на этапе проектирования. Потом будут проблемы. Вообще, лучше записывать. Разобрав-собрав один и тот-же узел (ось Z) подумал - ну почему я не пишу “технологическую карту”… В третий раз уже не подумал, а выругался матом на себя самого вслух.
  3. Ни одной детали станка абсолютно точной не бывает. Никогда. Это важно помнить на всех этапах.
    3.1 И собрать абсолютно точно ничего нельзя. Есть две параллельные детали? А как вы их выставите? А после того, как выставили, как закрепите? Об этом нужно думать с самого начала. И записывать все, записывать.
  4. Кнопка аварийной остановки есть самый главный атрибут станка. Она должна работать всегда. И ее работу надо проверять в первую очередь.

Фотографии будут завтра.

mohnik
Kitsok:

Гармошки мне больше нравятся. Тем более, что поглядев на реальную железяку (вот оно, отличие от 3Д модели 😉 ), видно, что место под гармошку есть!

На быструю руку,гармошку для защиты можно сделать из материала авто.фильтра

dberegovoy
mohnik:

На быструю руку,гармошку для защиты можно сделать из материала авто.фильтра

Ну раз такая песня может вообще из пыльников на рулевые рейки подобрать?