Плата MA3128 управляемая из Raspberry

ma3128 rpi stepmotor connected 

Второй проект для платы MA3128 будет немного сложнее, чем двоичный счетчик, описанный в предыдущей статье. Теперь я хочу установить нашу плату, как и задумывалось, на микрокомпьютер Raspberry Pi3. И даже подключу к плате шаговый микродвигатель. Плата MA3128 соединяется с Raspberry через 40ка пиновый разъем GPIO.

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

Сделать это не трудно. Нужно только определиться, какие выводы GPIO в Raspberry мы будем применять для этих целей.

Приступим к реализации этой идеи.


Я подумал, что было бы не плохо сперва придумать какую-то адресацию наших будущих аппаратных регистров в ПЛИС платы MA3128.
Допустим так:

  • 8ми битный регистр 0: светодиоды LED[8:0]
  • 10ти битный регистр 1: вывод на гребеночку IOA[9:0]
  • 10ти битный регистр 2: вывод на гребеночку IOB[9:0]
  • 4х битный регистр 3: вывод на шаговый двигатель MA[3:0]
  • 4х битный регистр 4: вывод на шаговый двигатель MB[3:0]
  • 4х битный регистр 5: вывод на шаговый двигатель MC[3:0]
  • 4х битный регистр 6: вывод на шаговый двигатель MD[3:0]

Поскольку внутренних регистров оказалось довольно много, то для их адресации требуется как минимум 3 бита.
Выберу для адресации регистров сигналы GPIO20, GPIO21, GPIO22 из Raspberry.
Как видно из списка выше, сама шина данных, записываемых в регистры должна быть 10ти битная.
Выбираю для шины данных сигналы GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15, GPIO16, GPIO17, GPIO18, GPIO19.
Ну и конечно нужен еще один сигнал записи wr, пусть будет GPIO23.
Идея такая: программа будет устанавливать сигнал записи wr (GPIO23) в ноль. Потом устанавливает шину данных и селектор регистра в нужные значения и поднимает сигнал записи в единицу. Схема в ПЛИС будет ловить фронт сигнала wr и как только фронт обнаружен запишет данные с шины данных по адресу выбранного регистра.

На микрокомпьютере Raspberry для питона уже существуют готовые библиотеки управления сигналами разъема GPIO.
Установить библиотеку можно просто командой
>sudo apt install python3-rpi.gpio

С помощью этой библиотеки можно индивидуально управлять каждым GPIO сигналом. Можно задавать его направление и значение. В этой статье я не буду останавливаться на коде программы на питоне. Рассмотрим лучше код для ПЛИС платы.

Логика работы в CPLD платы MA3128 не сложная. Вот и весь код Verilog:

module max(
	input wire CLK,
	input wire CLK2,
	output wire [7:0]LED,
	input wire  [1:0]KEY,
	output wire [3:0]MA,
	output wire [3:0]MB,
	output wire [3:0]MC,
	output wire [3:0]MD,
	output wire [9:0]IOA,
	output wire [9:0]IOB,
	//Raspberry GPIO pins
	inout wire [27:0]GPIO
);

wire wr_imp;
assign wr_imp = GPIO[23];

reg [1:0]wr;
always @(posedge CLK)
	wr <= {wr[0],wr_imp};

wire write; assign write = (wr==2'b01); //find raise edge

wire [2:0]sel;
assign sel = GPIO[22:20];

wire [9:0]bus;
assign bus = GPIO[19:10];

reg [7:0]led_reg; assign LED = led_reg;
reg [9:0]ioa_reg; assign IOA = ioa_reg;
reg [9:0]iob_reg; assign IOB = iob_reg;
reg [3:0]ma_reg;  assign MA  = ma_reg;
reg [3:0]mb_reg;  assign MB  = mb_reg;
reg [3:0]mc_reg;  assign MC  = mc_reg;
reg [3:0]md_reg;  assign MD  = md_reg;

always @(posedge CLK)
	if( write )
		case(sel)
			0: led_reg <= bus[7:0];
			1: ioa_reg <= bus[9:0];
			2: iob_reg <= bus[9:0];
			3: ma_reg  <= bus[3:0];
			4: mb_reg  <= bus[3:0];
			5: mc_reg  <= bus[3:0];
			6: md_reg  <= bus[3:0];
			7: led_reg <= bus[7:0];
		endcase

endmodule 

Всё, что выше описано, так в этом коде и реализовано. Выделение фронта сигнала GPIO23 и получившимся сигналом записи write производится запись данных с шины bus в выбранный sel регистр.

Для демонстрации проекта я написал программку на питоне hat-io.py. Работа программы продемонстрирована на этом видео:

На этом видео я запускаю свою питоновскую программу на Raspberry Pi3 с разными параметрами командной строки.

Программа может записывать байт в регистр светодиодов в режиме двоичного счета. Теперь уже сама программа на питоне выполняет счет и просто записывает результат в регистр платы. Чтобы увидеть демонстрацию этого режима работы запустите программу hat-io.py с параметром в командной строке counter.

Второй режим это тоже запись в регистр светодиодов, но записывается один единичный бит в байте и он перемещается от младшего бита к старшему и назад в цикле. Для программы hat-io.py параметр в командной строке bitmove.

Ну и последний режим работы моей демонстрационной программы это управление шаговым моторчиком 28BYJ-48 подключенным к порту MD[3:0] нашей платы MA3128.

Шаговый моторчик конечно подключен через его родной силовой модуль на базе микросхемы ULN2003, он обычно продается в комплекте с моторчиком. Если запустить программу hat-io.py с параметрами stepmotor F, то вал двигателя вращается в одну сторону, а если с параметром stepmotor B, то в другую.

Подключение сигналов моторчика к плате идет через угловые разъемы PIN HEADER 2x6. Сигналы там такие:

ma3128 motor signals

Моторчики бывают разные, есть с питанием +5В и есть с питанием +12В. Обратите внимание какие у вас.

Чтобы вал шагового моторчика вращался нужно на его выводы подавать сигналы в нужной последовательности. У моторчика 28BYJ-48 сигнальные выводы промаркированы цветами: розовый, оранжевый, красный, желтый и синий. Схема обмоток у него такая:

 28BYJ 48

В полношаговом режиме на обмотки нужно подавать такую последовательность импульсов:

 
Шаг Синий Розовый Желтый Оранжевый
0 1 1    
1   1 1  
2     1 1
3 1     1

 

В полушаговом режиме, который более плавный, последовательность импульсов в два раза длиннее:

Шаг Синий Розовый Желтый Оранжевый
0 1 1    
1   1    
2   1 1  
3     1  
4     1 1
5       1
6 1     1
7 1      

 

Вот эту последовательность и задает программа на питоне:

import sys
import signal
import RPi.GPIO as GPIO
import time

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

# Raspberry GPIO pins for write word 10bits, write register selector and write impulse
bits10 = [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]
bits8  = [ 10, 11, 12, 13, 14, 15, 16, 17 ]
bits4  = [ 10, 11, 12, 13 ]

#CPLD project gives 7 output registers selected by "sel" RPI GPIO pins:
#	0: led_reg
#	1: ioa_reg
#	2: iob_reg
#	3: ma_reg
#	4: mb_reg
#	5: mc_reg
#	6: md_reg
sel    = [ 20, 21, 22 ]

#RPI GPIO pin acts as write signal on raise edge
wr     = [ 23 ]

def set_pins_dir( pins, dir ) :
	for pin in pins :
		GPIO.setup( pin, dir )

set_pins_dir( bits10, GPIO.OUT )
set_pins_dir( sel,  GPIO.OUT )
set_pins_dir( wr,   GPIO.OUT )

def set_bits( pins, word ):
	for pin in pins :
		if word & 1 :
			GPIO.output(pin, GPIO.HIGH)
		else :
			GPIO.output(pin, GPIO.LOW)
		word = word >> 1


def write_word2reg( word, reg ) :
	global wr, sel, bits10, bits8, bits4
	GPIO.output( wr[0], GPIO.LOW)
	if reg==0 :
		bits = bits8
	elif reg == 1 or reg == 2 :
		bits = bits10
	else :
		bits = bits4
	set_bits( sel, reg )
	set_bits( bits, word )
	GPIO.output( wr[0], GPIO.HIGH)
	
def stepperHalf(cnt):
	if   cnt==0 :
		word=0b1100
	elif cnt==1 :
		word=0b0100
	elif cnt==2 :
		word=0b0110
	elif cnt==3 :
		word=0b0010
	elif cnt==4 :
		word=0b0011
	elif cnt==5 :
		word=0b0001
	elif cnt==6 :
		word=0b1001
	else :
		word=0b1000
	return word

def stepperFull(cnt):
	cnt=cnt&3
	if   cnt==0 :
		word=0b1100
	elif cnt==1 :
		word=0b0110
	elif cnt==2 :
		word=0b0011
	else :
		word=0b1001
	return word

#catch CTRL+C
def handler(signum, frame) :
	print("")
	print("Exit by CTRL+C")
	write_word2reg( 0, 0 )
	write_word2reg( 0, 1 )
	write_word2reg( 0, 2 )
	write_word2reg( 0, 3 )
	write_word2reg( 0, 4 )
	write_word2reg( 0, 5 )
	write_word2reg( 0, 6 )
	exit(1)
 
signal.signal(signal.SIGINT, handler)

#three types of RPI HAT IO DEMO: LED counter, LED bit move or Step motor drive
if len(sys.argv)<2 :
	print ('Need argument: "counter" or "bitmove" or "stepmotor"')
	exit(0)

if sys.argv[1]=="counter" :
	N=0
	while 1 :
		print( bin(N)[2:].zfill(8), end="\r" )
		write_word2reg( N, 0 )
		N=N+1
		time.sleep(0.1)
elif sys.argv[1]=="bitmove" :
	N=1
	DIR=1
	while 1 :
		print( bin(N)[2:].zfill(8), end="\r" )
		write_word2reg( N, 0 )
		if DIR :
			if N==128 :
				N=64
				DIR=0
			else :
				N=N<<1
		else:
			if N==1 :
				N=2
				DIR=1
			else :
				N=N>>1
		time.sleep(0.2)
elif sys.argv[1]=="stepmotor" :
	motor_dir = 1
	if len(sys.argv)==3 :
		if   sys.argv[2]=="F" :
			motor_dir = -1
			print("Forward")
		elif sys.argv[2]=="B" :
			motor_dir = 1
			print("Backward")
	#motor_port = 3 #PortA
	#motor_port = 4 #PortB
	#motor_port = 5 #PortC
	motor_port = 6 #PortD
	cnt=0
	while 1:
		#word=stepperFull(cnt)
		word=stepperHalf(cnt)
		print( bin(word)[2:].zfill(4), end="\r" )
		#print( bin(word)[2:].zfill(4))
		write_word2reg( word, motor_port )
		cnt=cnt+motor_dir
		cnt=cnt&7
		time.sleep(0.002)

Надеюсь, программа не выглядит сложной.

Весь проект для ПЛИС EPM3128ATC100 платы MA3128 находится в папке rpi-hat-io репозитория на github https://github.com/marsohod4you/MA3128

Проект выполняется в среде САПР Altera Quartus Web Edition 13.0sp1. Прошивка загружается в ПЛИС программатором MBFTDI.

 

Добавить комментарий