Почти L297 на Почти Verilog -e

Статья посвящена контроллеру шаговых двигателей L297. А также попытки оправдать свою лень и не идти покупать L297.
А заменить на ПЛИС которая у меня валяется без дела. Да и не только, также экономия играет свою роль, вместо покупки 3 драйверов установить одну ПЛИС при почти той же стоимости.

L297 устроенна довольно хитрым образом, и образ этот описан в даташите http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00000063.pdf

Но самое главное в даташите — это граф переходов. Выглядит он вот так:


Фактически с помощью графов можно построить совершенно любую схему на любой алгоритм. К моему удивлению все остальное в даташите не так интересно. Все что надо сделать построить этот граф на verilog.

Возможно конечно мой метод постройки такого автомата не идеален. (А я это знаю, еще со времен ПТЦА), Но тем не менее он работает.
При полном проходе по графу шаговый двигатель будет работать в режиме HALF-MODE (полушага) Кроме этого режима в L297 есть еще два. NORMAL MODE и Wave Mode. Последний я не описывал. Но я думаю, это небольшая потеря. Переключаются они с помощью вывода Half/Full

Код на Verilog для Half-Mode выглядит так:
module half_step_drv(dir,clk,a,na,b,nb,ena);
input dir;
input ena;
input clk;
output a,na,b,nb;
reg a,b,na,nb;
reg[0:3] step;
always@(posedge clk)
begin
if(ena)
begin
	 case(step)
		0 : 
			begin 
				a<=1'b0;
				na<=1'b1;
				b<=1'b0;
				nb<=1'b1;
					
			if(dir==1'b0)
				step=step+1'b1;
			else
				step=3'd7;
			end
		1 : 
			begin 
				a<=1'b0;
				na<=1'b0;
				b<=1'b0;
				nb<=1'b1;
				
			if(dir==1'b0)
				step=step+1'b1;
			else
				step=step-1'b1;
			
			end
		2 : 
			begin 
				a<=1'b1;
				na<=1'b0;
				b<=1'b0;
				nb<=1'b1;
		
			if(dir==1'b0)
				step=step+1'b1;
			else
				step=step-1'b1;
			
			end
		3 : 
			begin 
				a<=1'b1;
				na<=1'b0;
				b<=1'b0;
				nb<=1'b0;
			
			if(dir==1'b0)
				step=step+1'b1;
			else
				step=step-1'b1;
			
			end
		4 : 
			begin 
				a<=1'b1;
				na<=1'b0;
				b<=1'b1;
				nb<=1'b0;
		
		if(dir==1'b0)
				step=step+1'b1;
		else
				step=step-1'b1;
			
			end
		5 : 
			begin 
				a<=1'b0;
				na<=1'b0;
				b<=1'b1;
				nb<=1'b0;
		
		if(dir==1'b0)
				step=step+1'b1;
		else
				step=step-1'b1;	
			end
		6 : 
			begin 
				a<=1'b0;
				na<=1'b1;
				b<=1'b1;
				nb<=1'b0;
				
		if(dir==1'b0)
			step=step+1'b1;
		else
			step=step-1'b1;
			
			end
		7 : 
			begin 
				a<=1'b0;
				na<=1'b1;
				b<=1'b0;
				nb<=1'b0;
		
			if(dir==1'b0)
				step=3'b0;
			else
				step=step-1'b1;
		
			end
		default : 
			begin 
				a<=1'b0;
				na<=1'b1;
				b<=1'b0;
				nb<=1'b1;
			end
	endcase
	

end
else 
	begin 
		a<=1'bz;
		na<=1'bz;
		b<=1'bz;
		nb<=1'bz;
	end
end
endmodule

Где: Ena — Включение модуля, иначе на выходе Z состояние

CLK — Шаг двигателя

Dir — Направление

Работает это дело весьма просто. В середине есть счетчик команд step, с шагом которого выполняется определенная команда вершины графа, а именно выдача на выход определенного числа/состояния.

Normal Mode работает немного иначе. Фактически в нем пропущены все четные вершины графа.
ака вот так:



Из-за чего двигатель работает быстрее но и точность его от этого только страдает.

Вот более интересный код, созданный на основе Block Schem, Он качественно отличается от моего (благо оптимизатор работает) и над этим стоило бы когда нибудь задуматься.

Генерируется такой код почти также как и Block module через меню File -> Create/Update -> Create HDL Design File


module stepper_drv(
 DIR,
 CLK,
 ENA,
 A,
 NA,
 B,
 NB
);


input DIR;
input CLK;
input ENA;
output A;
output NA;
output B;
output NB;

reg SYNTHESIZED_WIRE_5;
reg SYNTHESIZED_WIRE_6;
wire SYNTHESIZED_WIRE_0;
wire SYNTHESIZED_WIRE_1;
wire SYNTHESIZED_WIRE_2;
wire SYNTHESIZED_WIRE_7;

assign A = ENA ? SYNTHESIZED_WIRE_5:1'bz ;
assign B = ENA ? SYNTHESIZED_WIRE_6:1'bz ;



assign SYNTHESIZED_WIRE_2 =  ~DIR;

assign SYNTHESIZED_WIRE_7 = SYNTHESIZED_WIRE_5 ^ SYNTHESIZED_WIRE_6;


always@(posedge CLK)
begin
if (ENA)
 begin
 SYNTHESIZED_WIRE_5 = SYNTHESIZED_WIRE_5 ^ SYNTHESIZED_WIRE_0;
 end
end


always@(posedge CLK)
begin
if (ENA)
 begin
 SYNTHESIZED_WIRE_6 = SYNTHESIZED_WIRE_6 ^ SYNTHESIZED_WIRE_1;
 end
end

assign SYNTHESIZED_WIRE_0 = SYNTHESIZED_WIRE_2 ^ SYNTHESIZED_WIRE_7;

assign SYNTHESIZED_WIRE_1 = DIR ^ SYNTHESIZED_WIRE_7;


assign NA = ENA ? ~SYNTHESIZED_WIRE_5:1'bz ;
assign NB = ENA ? ~SYNTHESIZED_WIRE_6:1'bz ;



endmodule


Данный модуль скомпилирован из вот этой схемы:


Кстати очень годная информация по шаговым двигателям описана здесь

http://en.wikibooks.org/wiki/Practical_Electronics/Stepper_Motors

Откуда и была взята эта схема

Собирая весь проект до кучи получается следующие:


Один такой блок занимает 15 макроячеек, так что с помощью epm3064 можно с легкостью заменить 4-е L297, если конечно учесть что не надо разные функции типа синхронизации итд.

Код кроме как на симуляторе еще нигде не тестировался, да и немного не то время для тестов. 4:34 утра.
Если будет интересно, то как протестирую выложу видео работы.
В конце прилаживаю проект Quartus 9.1

Это моя первая запись в сообществе так что любой «удар палкой в лоб» приветствуется.
  • +4
  • 05 ноября 2011, 04:57
  • letni
  • 1
Файлы в топике: CNC_Scanner.zip

Комментарии (20)

RSS свернуть / развернуть
А прикольно. Надо добавить еще сьем напряжения с шунта, для определения предельного тока и будет полный аналог. CNC шники будут рады.

Напругу, наверняка можно снять с помощью псевдо АЦП на копеечно компараторе.
0
Да не будет полный аналог. Нужно еще добавить ШИМ на отдельные выводы или на выводы моторов (по выбору) и установку режима ограничения тока, вот тогда будет похоже)))
А вообще, если городить свой велосипед, то лучше уже добавить и режим удержания, который включается через пару секунд простоя, вот тогда народ оценит)
0
Я думаю это не такая большая проблема, дело лишь в том что компараторы я в плисину ну не как не засуну, а вот сделать еще один вывод ENA на каждый такой модуль это идея.
Там то и ШИМ от внешнего сигнала прикрутить можно и…
0
По поводу ШИМа и ENAбля.
В данной версии оно работать будет не особо.
Лучше вытащить из always блока логику ena или вообще в топ-левеле сделать. Об этом я ниже говорил.
0
В целом идея не плоха, но профессиональные CNС на таких МС (L297) не строят.
Слишком все урезано получается, не хватает многих плюшек.

До этого такие драйвера строил на 88 Меге. Хватало с трудом, сейчас требования подросли, и уже все — переход на АРМ.
0
Ну выразился )))) Конечно, профессиональные строят на SIMODRIVE, SINAMICS, FAGOR и т.д. и т.п. ))
0
А обратную связь на чем делаешь? Если встроенный ацп использовать — успеет?
0
Ну да, не совсем верно выразился. Правильнее было бы сказать хоббийные серьезные станки.

Встроенный АЦП не успеет. Использую внешние компараторы.
0
Второй вариант явно предпочтительнее )))
А насчет первого… Ну там можно получше сделать.
Например, смена состояний автомата. Там в каждом кейсе есть

      if(dir==1'b0)
        step=step+1'b1;
      else
        step=step-1'b1;

Вынести наружу.
Еще смущает step=step+1'b1. Я сам еще учусь, но мне кажется, лучше будет, если написать вместо step+1'b1 номер того состояния, куда переходим. То есть, если из первого в второе, то step = 2. Надо проверить, да глянуть в RTL Viewer-e.
Алсо, конструкция
begin 
                a<=1'bz;
                na<=1'bz;
                b<=1'bz;
                nb<=1'bz;
        end

легко меняется на
{a,na,b,nb} <= 4'bzzzz;
В верилоге {} — шина, и её можно собрать из чего угодно. Полезная фишка,
0
Согласен, в таком виде в котором она есть, она не полностью отвечает абстрактному цифровому автомату.
Не знаю надо попробовать, в любом случае, если прорисовывать в голове схему из кода, то выходит следующее.

Верхний, мой вариант.
Нижний твой. фактически меняется счетчик на регистр, думаю разница не большая

А по поводу шин, да!, незнал :)
0
Упс, провтыкал схему.
http://imageshack.us/photo/my-images/7/l297aut.png/
0
Я переделал чуть твой код по-своему, а именно "… если из первого в второе, то step = 2… " и так далее. По макроячейкам ситуация не поменялась, но теперь умный квартус увидел тут конечный автомат. Теперь, если в RTL Viewer-e посмотреть, во что синтезируется данное кодище, то там можно увидеть FSM, к выходам которой подключена логика. Ща еще пару мыслей по улучшению попробую реализовать.
А вообще, советую почаще смотреть RTL-Viewer. Открывает глаза на многие вещи
0
Так, я еще улучшил кодище и теперь из твоей исходной схемы получилась такая няшка:
dl.dropbox.com/u/25100873/Image%202.png
Золотистая штука — это твой конечный автомат. Дважды щелкнешь — увидишь граф переходов и всё такое.
Как видно, его выходы через OR заведены на защелки. По каждому клоку они фиксируют новое значение.
И ушли ненужные защелки.
Вот новый исходник:
dl.dropbox.com/u/25100873/half_step_drv.v
Вкратце о том, что я делал.
1) Есть такое правило(не буду объяснять, почему, просто знай, что так надо): в одном always-блоке нельзя одновременно использовать блокирующие и неблокирующие присвоения(<= и =). Исправил везде на <=
2) Явно указал, из какого состояния куда переходить, и в итоге квартус увидел конечный автомат
3) выкинул нафиг из always блока логику ena.
и вот что сделал:
assign {a,na,b,nb} = (ena)? {a_r,na_r,b_r,nb_r}: 4'bzzzz;
Что делает конструкция вида assign a = (b)? c: d;
Если то, что в скобках, истинно, то это эквивалентно assign a = с иначе будет assign a = d;
a, na, b, nb — выходные проводники, которые при ena = 1 будут непрерывно выводить состояния внутренних защелок a_r, an_r и т.д., а при ena = 0 будут в High-Z.
А если бы это было бы в always блоке, то добавились бы еще защелки(одна, скорее всего), которые бы фиксировали состояние ena в момент фронта clk.
0
И таки можно уменьшить step. У тебя он объявлен как reg [3:0] step, хотя по факту reg [2:0] step
0
гы, только что заметил, что у тебя вообще reg [0:3] step.
Это не порядок.
Вообще, в скобках [MSB:LSB], то есть от старшего бита к младшему. И если у тебя будет, например, reg [0:7] counter, то старший бит счетчика у тебя будет с индексом 0, а младший — с индексом 7. Согласись, что некрасиво и нелогично )))
Гы-гы, больше придраться не к чему.
Ах да, tri-state буфера внутри модулей делать не стоит. Только в top-levele! То есть берешь, убираешь из модуля half-step логику enable, и просто между, например, выходом a модуля half-step и пином A0 ставишь примитив TRI, а уже на него заводишь ena.
Тогда и у квартуса к тебе претензий не будет(0 warnings), и все будет сделано по всем канонам ))
0
Вау! действительно стоящие дело сделал :)

Вот только я не понимаю сути Reg[0:3] -> Reg[3:0]. В общем-то разница с какой стороны ты начинаешь есть шоколадное печенька не велико. Важно лишь то что бы оно всегда было с одной стороны.
Другое дело так и начинаются срачи между Big-Endian и Little-Endian.

А по поводу всего остального, таки резонные замечания, спасибо!
0
По мне — в данном вопросе([3:0]) порядок должен быть во всем. У старшего бита старший индекс.
>>> Важно лишь то что бы оно всегда было с одной стороны.
Просто я еще ни в одном исходнике не видел такой нумерации. Принято писать [3:0] — и все это соблюдают.
Исключения — когда надо там перевернуть задом наперед что-нить. Но это уже не в объявлении reg, а в обращении к ним
0
То бишь… Ну привыкнешь ты писать [0:n].
Это будет проблемой для того, кто будет твой исходник юзать(ну если ты поделишься ;)).
Ну и обратная ситуация — взял где-нить какую-нить корку, а там оппа — [n:0].
0
Таки да, это просто дело привычки.
Счет то слева на право. Хотя более значимая цифра идет слева.
Это еще тот спор, меня за это еще в училище тихо ненавидели :)
0
Эх, давно хочу раскурить Verilog, да все нет то времени, то мотивации… А статье +1.
0
  • avatar
  • _YS_
  • 05 ноября 2011, 10:58
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.