5-Microstep Drive. Xilinx CPLD

Impartial

Схему сейчас разрабатываю на EP1K30TC144 потому что много в наличии.
Программирование по PPA. Штатное по PS.
Разводка поддерживает и то и другое.
Занимаюсь сервоприводом.
Вам какой исходник?
Вот штатный на сервопривод.
Тут три модуля
1 Servo
2 Quad
3 WDT
Компилится в Квартусе без проблем.
На отладчике тоже вроде все ОК.
На железе еще не пробовал.

// This is a component of pluto_servo, a PWM servo driver and quadrature
// counter for emc2
// Copyright 2006 Jeff Epler <jepler@unpythonic.net>
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

module pluto_servo(clk, led, nConfig, epp_nReset, pport_data, nWrite, nWait, nDataStr,
nAddrStr, dout, din, quadA, quadB, quadZ, up, down);
parameter QW=14;
input clk;
output led, nConfig;
inout [7:0] pport_data;
input nWrite;
output nWait;
input nDataStr, nAddrStr, epp_nReset;
wire do_tristate;
reg[9:0] real_dout; output [9:0] dout = do_tristate ? 10’bZZZZZZZZZZ : real_dout;
input [7:0] din;
input [3:0] quadA;
input [3:0] quadB;
input [3:0] quadZ;
wire[3:0] real_up; output [3:0] up = do_tristate ? 4’bZZZZ : real_up;
wire[3:0] real_down; output [3:0] down = do_tristate ? 4’bZZZZ : real_down;
reg Zpolarity;

wire [2*QW:0] quad0, quad1, quad2, quad3;

wire do_enable_wdt;
wire pwm_at_top;
wdt w(clk, do_enable_wdt, pwm_at_top, do_tristate);

// PWM stuff
// PWM clock is about 20kHz for clk @ 40MHz, 11-bit cnt
reg [10:0] pwmcnt;
wire [10:0] top = 11’d2046;
assign pwm_at_top = (pwmcnt == top);
reg [15:0] pwm0, pwm1, pwm2, pwm3;
always @(posedge clk) begin
if(pwm_at_top) pwmcnt <= 0;
else pwmcnt <= pwmcnt + 11’d1;
end

wire [10:0] pwmrev = {
pwmcnt[4], pwmcnt[5], pwmcnt[6], pwmcnt[7], pwmcnt[8], pwmcnt[9],
pwmcnt[10], pwmcnt[3:0]};
wire [10:0] pwmcmp0 = pwm0[14] ? pwmrev : pwmcnt;
// wire [10:0] pwmcmp1 = pwm1[14] ? pwmrev : pwmcnt;
// wire [10:0] pwmcmp2 = pwm2[14] ? pwmrev : pwmcnt;
// wire [10:0] pwmcmp3 = pwm3[14] ? pwmrev : pwmcnt;
wire pwmact0 = pwm0[10:0] > pwmcmp0;
wire pwmact1 = pwm1[10:0] > pwmcmp0;
wire pwmact2 = pwm2[10:0] > pwmcmp0;
wire pwmact3 = pwm3[10:0] > pwmcmp0;
assign real_up[0] = pwm0[12] ^ (pwm0[15] ? 1’d0 : pwmact0);
assign real_up[1] = pwm1[12] ^ (pwm1[15] ? 1’d0 : pwmact1);
assign real_up[2] = pwm2[12] ^ (pwm2[15] ? 1’d0 : pwmact2);
assign real_up[3] = pwm3[12] ^ (pwm3[15] ? 1’d0 : pwmact3);
assign real_down[0] = pwm0[13] ^ (~pwm0[15] ? 1’d0 : pwmact0);
assign real_down[1] = pwm1[13] ^ (~pwm1[15] ? 1’d0 : pwmact1);
assign real_down[2] = pwm2[13] ^ (~pwm2[15] ? 1’d0 : pwmact2);
assign real_down[3] = pwm3[13] ^ (~pwm3[15] ? 1’d0 : pwmact3);

// Quadrature stuff
// Quadrature is digitized at 40MHz into 14-bit counters
// Read up to 2^13 pulses / polling period = 8MHz for 1kHz servo period
reg qtest;
wire qr0, qr1, qr2, qr3;
quad q0(clk, qtest ? real_dout[0] : quadA[0], qtest ? real_dout[1] : quadB[0], qtest ? real_dout[2] : quadZ[0]^Zpolarity, qr0, quad0);
quad q1(clk, quadA[1], quadB[1], quadZ[1]^Zpolarity, qr1, quad1);
quad q2(clk, quadA[2], quadB[2], quadZ[2]^Zpolarity, qr2, quad2);
quad q3(clk, quadA[3], quadB[3], quadZ[3]^Zpolarity, qr3, quad3);

// EPP stuff
wire EPP_write = ~nWrite;
wire EPP_read = nWrite;
wire EPP_addr_strobe = ~nAddrStr;
wire EPP_data_strobe = ~nDataStr;
wire EPP_strobe = EPP_data_strobe | EPP_addr_strobe;

wire EPP_wait; assign nWait = ~EPP_wait;
wire [7:0] EPP_datain = pport_data;
wire [7:0] EPP_dataout; assign pport_data = EPP_dataout;

reg [4:0] EPP_strobe_reg;
always @(posedge clk) EPP_strobe_reg <= {EPP_strobe_reg[3:0], EPP_strobe};
wire EPP_strobe_edge1 = (EPP_strobe_reg[2:1]==2’b01);

// reg led;

assign EPP_wait = EPP_strobe_reg[4];
reg[4:0] addr_reg;
reg[7:0] lowbyte;

always @(posedge clk)
if(EPP_strobe_edge1 & EPP_write & EPP_addr_strobe) begin
addr_reg <= EPP_datain[4:0];
end
else if(EPP_strobe_edge1 & !EPP_addr_strobe) addr_reg <= addr_reg + 4’d1;
always @(posedge clk) begin
if(EPP_strobe_edge1 & EPP_write & EPP_data_strobe) begin
if(addr_reg[3:0] == 4’d1) pwm0 <= { EPP_datain, lowbyte };
else if(addr_reg[3:0] == 4’d3) pwm1 <= { EPP_datain, lowbyte };
else if(addr_reg[3:0] == 4’d5) pwm2 <= { EPP_datain, lowbyte };
else if(addr_reg[3:0] == 4’d7) pwm3 <= { EPP_datain, lowbyte };
else if(addr_reg[3:0] == 4’d9) begin
real_dout <= { EPP_datain[1:0], lowbyte };
Zpolarity <= EPP_datain[7];
qtest <= EPP_datain[5];
end
else lowbyte <= EPP_datain;
end
end

reg [31:0] data_buf;

always @(posedge clk) begin
if(EPP_strobe_edge1 & EPP_read && addr_reg[1:0] == 2’d0) begin
if(addr_reg[4:2] == 3’d0) data_buf <= quad0;
else if(addr_reg[4:2] == 3’d1) data_buf <= quad1;
else if(addr_reg[4:2] == 3’d2) data_buf <= quad2;
else if(addr_reg[4:2] == 3’d3) data_buf <= quad3;
else if(addr_reg[4:2] == 3’d4)
data_buf <= {quadA, quadB, quadZ, din};
end
end

// the addr_reg test looks funny because it is auto-incremented in an always
// block so “1” reads the low byte, "2 and “3” read middle bytes, and “0”
// reads the high byte I have a feeling that I’m doing this in the wrong way.
wire [7:0] data_reg = addr_reg[1:0] == 2’d1 ? data_buf[7:0] :
(addr_reg[1:0] == 2’d2 ? data_buf[15:8] :
(addr_reg[1:0] == 2’d3 ? data_buf[23:16] :
data_buf[31:24]));

wire [7:0] EPP_data_mux = data_reg;
assign EPP_dataout = (EPP_read & EPP_wait) ? EPP_data_mux : 8’hZZ;
assign do_enable_wdt = EPP_strobe_edge1 & EPP_write & EPP_data_strobe & (addr_reg[3:0] == 4’d9) & EPP_datain[6];
assign qr0 = EPP_strobe_edge1 & EPP_read & EPP_data_strobe & (addr_reg[4:2] == 3’d0);
assign qr1 = EPP_strobe_edge1 & EPP_read & EPP_data_strobe & (addr_reg[4:2] == 3’d1);
assign qr2 = EPP_strobe_edge1 & EPP_read & EPP_data_strobe & (addr_reg[4:2] == 3’d2);
assign qr3 = EPP_strobe_edge1 & EPP_read & EPP_data_strobe & (addr_reg[4:2] == 3’d3);
assign led = do_tristate ? 1’BZ : (real_up[0] ^ real_down[0]);
assign nConfig = epp_nReset; // 1’b1;
endmodule

// This is a component of pluto_servo, a PWM servo driver and quadrature
// counter for emc2
// Copyright 2006 Jeff Epler <jepler@unpythonic.net>
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1507 USA

module quad(clk, A, B, Z, zr, out);
parameter W=14;
input clk, A, B, Z, zr;
reg [(W-1):0] c, i; reg zl;
output [2*W:0] out = { zl, i, c };
// reg [(W-1):0] c, i; reg zl;

reg [2:0] Ad, Bd;
reg [2:0] Zc;
always @(posedge clk) Ad <= {Ad[1:0], A};
always @(posedge clk) Bd <= {Bd[1:0], B};

wire good_one = &Zc;
wire good_zero = ~|Zc;
reg last_good;

wire index_pulse = good_one && ! last_good;

wire count_enable = Ad[1] ^ Ad[2] ^ Bd[1] ^ Bd[2];
wire count_direction = Ad[1] ^ Bd[2];

always @(posedge clk)
begin
if(Z && !good_one) Zc <= Zc + 2’b1;
else if(!good_zero) Zc <= Zc - 2’b1;
if(good_one) last_good <= 1;
else if(good_zero) last_good <= 0;
if(count_enable)
begin
if(count_direction) c <= c + 1’d1;
else c <= c - 1’d1;
end
if(index_pulse) begin
i <= c;
zl <= 1;
end else if(zr) begin
zl <= 0;
end
end
endmodule

// This is a component of pluto_servo, a PWM servo driver and quadrature
// counter for emc2
// Copyright 2006 Jeff Epler <jepler@unpythonic.net>
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

module wdt(clk, ena, cnt, out);
input clk, ena, cnt;
output out;
reg [6:0] timer;
wire timer_top = (timer == 7’d127);
reg internal_enable;
wire out = internal_enable && timer_top;

always @(posedge clk) begin
if(ena) begin
internal_enable <= 1;
timer <= 0;
end else if(cnt && !timer_top) timer <= timer + 7’d1;
end
endmodule

ATLab

Может, все же в новую ветку, а? Это же не трудно!
Как ни интересно, но это не имеет отношения к 5-Microstep Drive.

********

11 days later
arisov77

Может кому пригодиться, вот здесь soel.ru/cms/f/?/364218.pdf есть статейка «Шаговый двигатель, управляемый устройством на ПЛИС»

mura
arisov77:

Может кому пригодиться, вот здесь soel.ru/cms/f/?/364218.pdf есть статейка «Шаговый двигатель, управляемый устройством на ПЛИС»

Автор в реале с ПЛИС не работал, даже начинающий ПЛДшник такого себе не позволит.

7 days later
boldive

Слепил. Пойду смотреть откуда дым повалит😲

Силовая часть сделана на связке IR2104 + STP30NF20
Две отптопары HCPL2531 (для STEP, DIR) и PS2501 (Enable) еще не запаял. Они будут с обратной стороны платы (в левом верхнем углу на картинке видны посадочные места).

13 days later
boldive

Еще немножко доработал схему и теперь все работает. Сделал какое-то подобие морфинга. Вначале было 1/10 => 1/4 => 1/2 => FullStep а потом сделал по другому. Стал синусоиду вытягивать по экспоненте (опорное напряжение стало походить на трапецию). Что-то так мне больше нравится. Более плавно. Максимальная скорость которую могу вытащить из Mach3 3750мм/мин (1/10 микрошаг, 5мм винт) и на этой скорости крутит. Драйвер и мотор греются не шибко сильно. По ходу работы из мотора слышно какое-то постукивание, не механическое, явно электрического происхождения. Примерно каждые 3-6сек слышен такой “ТУК”. Надо на станке погонять и посмотреть что там с пропуском шагов, если кончно будет. Где-то на 2300мм/мин идет резонанс. Вот с этим облом. Тут только программой не обойдешься, надо менять схемотехнику. Только непонятно откуда начинать. Можно сравнивать ток обмотки мотора (в идеале экспонента) с какой-то опорной экспонентой и в случае расхождения выдавать сигнал на CPLD (при резонансе экспонента начинает вытягиваться в синусоиду). А вот что дальше менять в алгоритме работы пока не знаю.

to ATLab. Посмотрел я на картинки тока в обмотках двигателя. Такие-же как и в G203V (это когда резонанса нет). Практически все один в один. Форму тока при резонансе надо снимать на видео, тогда будет видно как она “раздувается”.

ATLab
boldive:

… Форму тока при резонансе надо снимать на видео, тогда будет видно как она “раздувается”.

Что значит “раздувается”? Что раздувается?

boldive
ATLab:

Что значит “раздувается”? Что раздувается?

Пост №96. Последние две картинки показывают форму тока когда резонанса нет.
Перед переходом в резонанс форма тока начинает “раздуваться” и больше походить на синусоиду чем на экспоненту.

А при самом резонансе уже почти синусоида.

Ну видео как все это происходит тут:

На 53сек четко виден переход в резонанс. Весь этот “визжащий-надрывный” звук из ролика когда мотор в резонансе и вал не крутится.

13 days later
boldive

Покрутил на станке. 1/10 микрошаг. Винт 5мм. Голый мотор уходил в резонанс на скорости 2300мм/мин. Воткнул на ось Y. Теперь полоса резонанса с 2400 до 3100 мм/мин. Выше 3100мм/мин и до 3750мм/мин (Mach3 больше не дает) никаких проблемм.

Порылся в интернете и понял что тема борьбы с резонансом “засекречена”. Есть общие идеи с которых можно начинать, но как именно это реализовывать в системах контроля с open-loop под завесой тайны.

На http://cnczone.com нашел только единственную фразу проливающую свет на решение проблеммы.
The midband resonance circuit adds a derivative component (+80 degrees
phase lead) to the system phase angle to eliminate parametric
resonance. It involves sensing the rate of motor load change
(derivative of motor torque) which then phase modulates the internal
step pulse timing.

Может у кого есть еще какие-то практические мнения по этому поводу?

ATLab
  1. Самое простое - посмотреть как это делается в Gecko. Проследить по плате драйвера кусок схемы на ОУ от измерительных резисторов до компараторов и попытаться понять, что там творится.
  2. Поискать патенты.
boldive

Как сделано в геко посмотрел, даже схемку нарисовал. Там с каждого current sense резистора через сопротивление в 10К (оба этих резистора соединены вместе) снимается сигнал (получается суммарный сигнал) и идет на ОУ который является фильтром низких частот, а с него на компаратор. С компаратора на CPLD. Что дальше происходит в программе пока непонятно. Причем опорное напряжение для компаратора тоже формируется от CPLD.

Патенты искал. Пока ничего путного не нашел.

mura
boldive:

Там с каждого current sense резистора через сопротивление в 10К (оба этих резистора соединены вместе) снимается сигнал (получается суммарный сигнал)

Может это для защиты цепь?
Или для поддержания “среднего” тока?

boldive

Защита там по другому выполнена и к CPLD не имеет отношения. “Защелка” на двух элементах НЕ. На входе сигнал от терминала “Enable”. На выходе сигнал SD для IR2104. Плюс, туда еще приходит сигнал с токового резистора стоящего в цепи общего питания драйвера (собственно защита).

ATLab
boldive:

Как сделано в геко посмотрел… идет на ОУ который является фильтром низких частот, а с него на компаратор. С компаратора на CPLD…

Где-то у меня валялся китаец, драный с Геко, сделанного еще на логике.
Если найду, в выходные попробую посмотреть.

А этот ФНЧ никакую фазу не двигает? По идее, смысл то в введении доп. фазового сдвига, чтобы убрать условие самовозбуждения.

boldive
ATLab:

А этот ФНЧ никакую фазу не двигает? По идее, смысл то в введении доп. фазового сдвига, чтобы убрать условие самовозбуждения.

Выход с компаратора идет на CPLD. Я вот думаю, надо наверно покрутить мотор гековским G203V и посмотреть при каких ситуациях на выходе компаратора появляются импульсы и что идет с выхода CPLD для формирования опорного напряжения.

Ничего кроме как сдвигать фазу на 10°-15° в голову не приходит. Теперь попутно еще вопрос, а как распознать момент когда надо эту самую фазу сдвигать?

ATLab
ATLab:

Где-то у меня валялся китаец, драный с Геко, сделанного еще на логике.Если найду, в выходные попробую посмотреть…

Не нашел 😦 Куда-то заиграл…

Impartial

Может это поможет, нам помогло
www.electroprivod.ru/microstep.htm
собственно эта часть
" если ни одна часть магнитной цепи двигателя не насыщается."

1 month later
VladimirVA
boldive:

Как сделано в геко посмотрел, даже схемку нарисовал. Там

1)с каждого current sense резистора через сопротивление в 10К (оба этих резистора соединены вместе) снимается сигнал (получается суммарный сигнал) и идет на ОУ который является фильтром низких частот, а с него на компаратор. С компаратора на CPLD. Что дальше происходит в программе пока непонятно.
2)Причем опорное напряжение для компаратора тоже формируется от CPLD

  1. Получается усреднение сигналов, которое характеризует разность сигналов (причем модуль). Например, если смотреть в точке (по времени) где сигналы должны быть одинаковы, мы получим рассогласование фаз или насыщение и т.д.
  2. Наверное постоянный сигнал с CPLD подавать бессмысленно. Вероятно, подается ШИМ на интегратор. Тогда это просто ЦАП, который используется как уставка регистрации рассогласования сигналов.

Кроме того, есть вопрос по приведенным осциллограммам вашей платы. У меня не было времени изучить программу управления ключами, но мне показалось, глядя на осциллограммы, что регулировка тока напрямую подключена к компараторам, стабильность которой определяется наводками. Если это так, может попробовать усреднять сигналы с компараторов для исключения влияния наводок. Практически, можно откалибровать зависимость тока от частоты, для конкретнй нагрузки и использовать затабулированные значения для регулировки тока в обмотке. Интересно, как тогда будут выглядеть осциллограммы?

boldive

Сегодня покрутил драйвера на станке. Работает без проблемм. Поскольку станок дает нагрузку на моторы то с резонансом на станке еще не столкнулся.

Помех от совместной работы драйверов и шпинделя (BOSH) также не наблюдалось. Наверно все доработки которые внес в схему драйвера и платы опторазвязки сделали свое дело.

Остаются еще недоделки с механикой, да и с драйверами еще можно долго играться, но в целом на станке уже можно смело строгать и пилить. Чем скоро и займемся.

mura
boldive:

Наверно все доработки которые внес в схему драйвера и платы опторазвязки сделали свое дело.

А можно подробнее о доработках.

boldive
mura:

А можно подробнее о доработках.

Добавил фильтры между оптопарой и входами CPLD.


После платы опторазвязки воткнул буферные микросхемы 74LS244. Теперь выходное сопротивление значительно понизилось, соответственно наводимые помехи шунтируются на блок питания опторазвязки.

74LS244 ставил потому что они у меня уже были. Лучше будет поставить что-то с тригером Шмитта по входу. Например 74HC7541 или что-то похожее. Если используется сигнал Enable для всех драйверов то желательно на этот сигнал повесить два буфера, а то выходного тока может на всех не хватить.