Мой прожектикъ
Модбас.
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 (нужно осцилоскопом поглядеть фронты и поискать более быстродействующие оптопары), но меня устраивает 😉
Изготавливалось методом фотолитографии на сухой фоторезист, получится ли изготовить ЛУТом - не знаю, думаю, что да.
Kitsok,
-
насчёт модбаса - тема очень интересная. Производительности его хватает?
-
Вы как-то будете защищать винт ШВП и направляющие от попадания стружки? Насколько это вобще необходимо/желательно?
to all:
- я пока делаю управление для станка. Изначально хотел расположить драйверы двигателей в непосредственной близости к ШД. Рядом поставить - не проблема. Сейчас много драйверов, практически не греющихся, на полевиках. Остаётся вопрос: есть ли в этом какой-то смысл.
avric, Производительности 100% хватает на 38400, на 4800 его хватит для аварийной остановки в случае попытки самоубийства станка (концевики на осях + E-Stop). Конечно, калибровать home на 4800 будет очень медленно.
Защищать ШВП очень и очень желательно, но я пойду другим путем - рядом со шпинделем будет пылесос 😉
Насчет выноса силовой части поближе к ШД - ни больших минусов ни плюсов не вижу, вполне имеющая право на существование схема.
Спасибо. Вопрос, как определяются выходы Dir и Step?
- Вы как-то будете защищать винт ШВП и направляющие от попадания стружки? Насколько это вобще необходимо/желательно?
Хотя вопрос не мне, но, думаю, нелишним будет высказаться 😉
Защищать от мусора надо всё, что не относится к рабочей зоне.
Или, скажем так- по возможности всё.
ШВП и направляющие в работе всегда в слое смазки. На эту смазку имеет свойство налипать всякая пылинка. Со временем там уже становится много пыли, стружки и т.п. мусора. И так до тех пор, пока подклинывать не начнёт.
Зачем этого дожидаться? Лучше сразу защиту продумать.
Зачем этого дожидаться? Лучше сразу защиту продумать.
… или мыть и смазывать после каждого сеанса 😉
Вцелом - согласен, но вменяемых защит такой схему станка мне не встречалось. Можно попробовать телескопическую спиральную защиту, но она отъест существенный рабочий ход.
Спасибо. Вопрос, как определяются выходы Dir и Step?
Да никак 😉 Этот контроллер - для дискретных входов и выходов, а не для шаговых двигателей 😉 У меня как бы два контроллера - пурелоджик для управления моторами и мой для входов-выходов.
… или мыть и смазывать после каждого сеанса 😉
Это через некоторое время надоест…😉
Вцелом - согласен, но вменяемых защит такой схему станка мне не встречалось. Можно попробовать телескопическую спиральную защиту, но она отъест существенный рабочий ход.
Не обязательно. С внешних сторон можно поставить жёсткие щитки Г-образного сечения(гнутые из стали, пластика). С внутренней стороны(где крепление портала к винтам) шторка-гармошка из ПВХ(кожзама и т.п.).
можно поставить короба с прорезями на каретки и т.д. и в них подать сжатый воздух с выхода тогоже пылесоса (после фильтрации) и тогда туда пыль не залетит.
Гармошки мне больше нравятся. Тем более, что поглядев на реальную железяку (вот оно, отличие от 3Д модели 😉 ), видно, что место под гармошку есть!
Интересная конструкция. Подскажите если не трудно габариты каретки Зет (длина, ширина, высота, вес). Очень интересно как он в работе, результаты уже есть?
Гармошки мне больше нравятся. Тем более, что поглядев на реальную железяку (вот оно, отличие от 3Д модели ), видно, что место под гармошку есть! google_ad_section_end
Да ну и чем же лучше?? Короб никуда не двигается и неизнашеввется, всегда есть доступ чтоб смазать трущееся детали, тем более что пылик вы всеравно ставить будете. Кроме того где взять эти гармошки и сколько они служить будут это еще тот вопрос.
Короб никуда не двигается
Тогда я не понял, куда Вы предлагаете поставить короб.
dberegovoy, хотите модель в SolidWorks? Размеров там много разных…
Согласно солиду (а он в общем довольно точно считает вес), ось Z вместе с подшипниками и гайкой Y, весит 6.9 кг.
Ширина - 130, глубина (наибольшая) 186, высота min:453.55, max 496.05.
модель SolidWorks?
Спасибо, от солида не откажусь, выложите в тему может еще кому пригодится.
Станок уже в работе? Есть готовые образцы изделий? Очень интересно.
Тогда я не понял, куда Вы предлагаете поставить короб
Например по Х делаем П- образную деталь изз жести надеваем ее на ходовой винт с напраляющей, сбоку делаем прорезь в которой ездит плита которя соиденяет гайку с боковинами. У вас останется щель гдето 20мм, из которой будет под давлением выходить воздух, и туда ниче не попадет.
По У тоже самое из жести закрываем все (между стойками) оставляем прорези на подшипники, по Z нуно думать там сложнее, но если станок уже готов то на готовом легче все посмотреть. Во все короба подводим воздуховодами например от пылесоса воздух, и все дело в шляпе, туда пыль никогда не попадет.
Например по Х делаем П- образную деталь изз жести надеваем ее на ходовой винт с напраляющей, сбоку делаем прорезь
Вкурил.
Зачет!!! Буду рисовать.
Станок еще не в работе, во вторник поедет в разобранном виде на дачу, где будет собираться окончательно и запускаться.
Как и обещал, чертежи лежат на nigde.ru/cnc1.rar
Я даже не знаю, какая лицензия подразумевает “отчет по прочтении” 😉, но, друзья, вы хоть свистните, все ли понятно или нет.
Сегодня обнаружил, что в текущей (той, что в архиве) модели все собираемо при соблюдении последовательности. Не хватает отверстий под концевики, но они все встают нормально, очень забавно Mach3 гоняет оську при поиске home.
Проблема на текущий момент - в ногах под станину. Как появятся - будет фотоотчет.
9 месяцев от идеи до первого результата. Как ребенка родил. Точнее, за это время второй сын успел родиться, вырости и уже начать орать, как потерпевший по всякому поводу 😉
Сегодня запутался в настройках Мача, плюс телефонный звонок во время reference axis, и - привет, разобрал(ся) подшипниковый узел. Не я виноват - крутящий момент 😉
Потом, после стакана виски, вдумывания в конструкцию и переделывания “мозгов” Мача - был получен первый результат. С подачей еще нужно разобраться, но надписи вполне различимы 😉
Поздравьте меня, други, я перехожу из категории “размышлятелей” в категорию “создавших”. Это приятно.
Предварительные выводы:
- При проектировании надо очень очень очень много думать о том, из чего оно будет собираться. Какая у “этого” точность. Как оно ведет себя при перепадах температуры. Где это купить. Что вы купите, заказав это (!!!). Не нужно тут особенно комплексовать, но и забывать об этих факторах нельзя. Это может быть фатально для проекта.
- Сборка. Нужно думать о том, как вы будете собирать свой станок на этапе проектирования. Желательно… нет, необходимо!!! продумать - как будут проходить кабели, как будут крепиться концевики, как будут идти кабельгоны, и т.д. и т.п. уже на этапе проектирования. Потом будут проблемы. Вообще, лучше записывать. Разобрав-собрав один и тот-же узел (ось Z) подумал - ну почему я не пишу “технологическую карту”… В третий раз уже не подумал, а выругался матом на себя самого вслух.
- Ни одной детали станка абсолютно точной не бывает. Никогда. Это важно помнить на всех этапах.
3.1 И собрать абсолютно точно ничего нельзя. Есть две параллельные детали? А как вы их выставите? А после того, как выставили, как закрепите? Об этом нужно думать с самого начала. И записывать все, записывать. - Кнопка аварийной остановки есть самый главный атрибут станка. Она должна работать всегда. И ее работу надо проверять в первую очередь.
Фотографии будут завтра.
С пополнением!!! Молодчага, ждем фоток.
Гармошки мне больше нравятся. Тем более, что поглядев на реальную железяку (вот оно, отличие от 3Д модели 😉 ), видно, что место под гармошку есть!
На быструю руку,гармошку для защиты можно сделать из материала авто.фильтра