Второй проект для платы 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. Сигналы там такие:
Моторчики бывают разные, есть с питанием +5В и есть с питанием +12В. Обратите внимание какие у вас.
Чтобы вал шагового моторчика вращался нужно на его выводы подавать сигналы в нужной последовательности. У моторчика 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.
Подробнее...