Commit d7a1849e authored by Mikaël BRIDAY's avatar Mikaël BRIDAY
Browse files

first virtual lab

parent 53efa700
......@@ -13,3 +13,13 @@ add_custom_target(flash
DEPENDS ${PROJECT_NAME}.bin
COMMAND st-flash --reset write ${PROJECT_NAME}.bin 0x8000000
)
add_custom_target(qemu-run
DEPENDS ${PROJECT_NAME}.bin
COMMAND qemu-system-arm -M nucleo32_f303 -kernel ${PROJECT_NAME}.bin -nographic
)
add_custom_target(qemu-gdb
DEPENDS ${PROJECT_NAME}.bin
COMMAND qemu-system-arm -M nucleo32_f303 -kernel ${PROJECT_NAME}.bin -nographic -S -gdb tcp::4242
)
You need to make a link so that the qemu program can be run from everywhere. Copy and past the line in a terminal (in the current folder), root access required:
sudo ln -s /usr/local/bin/qemu-system-arm qemu-system-arm
#! /usr/bin/env python3
# -*- coding: UTF-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys,time
import threading
# Communication part
import struct
pipc_magic = 0xdeadbeef
import posix_ipc as pipc
mq_to_qemu = pipc.MessageQueue("/to_qemu",flags=pipc.O_CREAT, read=False, write=True)
mq_from_qemu = pipc.MessageQueue("/from_qemu",flags=pipc.O_CREAT, read=True, write=False)
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args,**kwargs)
self.setWindowTitle("QEmu GPIO interface for STM32F303")
self.gpios = {'A':[],'B':[],'C':[],'D':[],'F':[]}
self.gpioIds = {'A':0,'B':1,'C':2,'D':3,'F':4}
layoutV = QVBoxLayout()
self.palOn = QPalette()
self.palOn.setColor(QPalette.Base,Qt.darkGreen)
self.palOff = QPalette()
self.palOff.setColor(QPalette.Base,Qt.darkRed)
self.palDis = QPalette()
self.palDis.setColor(QPalette.Base,Qt.gray)
for gpio in self.gpios:
#Gpio name
gbox=QGroupBox("GPIO"+gpio)
#Gpio pin state
layout = QHBoxLayout()
for i in range(16):
chkbox=QCheckBox("P"+str(gpio)+str(i))
chkbox.setAutoFillBackground(True)
chkbox.setPalette(self.palOff)
self.gpios[gpio].append(chkbox)
layout.addWidget(chkbox)
#We have to copy arguments (gpio=gpio, i=i) so that
#there is a new lambda for each iteration
chkbox.stateChanged.connect(lambda x, gpio=gpio,i=i: self.buttonChecked(x,self.gpioIds[gpio],i))
gbox.setLayout(layout)
layoutV.addWidget(gbox)
self.setGPIO(gpio,0xFFFF,0) #set all as output / 0
label = QLabel("checkbox in color (red/green) are configured as output. Checkbox in grey are inputs and can be checked/unchecked")
label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
layoutV.addWidget(label)
widget = QWidget()
widget.setLayout(layoutV)
self.setCentralWidget(widget)
def buttonChecked(self,state,gpioId,pin):
""" called when a checkbox is checked """
print("send msg: GPIO "+str(gpioId)+", pin "+str(pin)+', state '+str(state==Qt.Checked) )
s=struct.pack("=IIII",pipc_magic,pin,state==Qt.Checked,gpioId)
mq_to_qemu.send(s)
def setGPIO(self,port,dir_mask,output):
for pin in range(16):
chkbox = self.gpios[port][pin]
out = (dir_mask >> pin) & 1
chkbox.setCheckable(not out)
if out:
state = (output >> pin) & 1
chkbox.setChecked(state)
if state:
pal = self.palOn
else:
pal = self.palOff
else:
pal = self.palDis
chkbox.setPalette(pal)
app = QApplication(sys.argv)
window = MainWindow()
def receiver():
while True:
msg = mq_from_qemu.receive()
mg, changed_out, dir_mask,output,gpio = struct.unpack("=IHHHH",msg[0])
#print("mg=",mg," pin=",pin," gpio=",gpio," state=",state)
if mg != pipc_magic:
raise Exception("Wrong magic number in GPIO IPC message")
name = ['A','B','C','D','F']
if gpio < 5:
window.setGPIO(name[gpio],dir_mask,output) #TODO: thread safe?
#required if we get too much messages to let time for the UI.
time.sleep(.01)
window.show()
thread = threading.Thread(target=receiver)
thread.daemon = True
thread.start()
app.exec_() #event loop
#! /usr/bin/env python3
# -*- coding: UTF-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys,time
import threading
# Communication part
import struct
pipc_gpio_magic = 0xdeadbeef
pipc_adc_magic = 0xcafecafe
import posix_ipc as pipc
mq_to_qemu = pipc.MessageQueue("/to_qemu",flags=pipc.O_CREAT, read=False, write=True)
mq_from_qemu = pipc.MessageQueue("/from_qemu",flags=pipc.O_CREAT, read=True, write=False)
#for retinal persistence.
led = [False,False,False,False,False,False]
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args,**kwargs)
self.setWindowTitle("QEmu Stepper lab")
self.leds = [] #PB0, PB3 => tuple (pin,chkbox)
self.buttons = [] #PB6, PB1 => tuple (pin,chkbox)
self.state = 0 #stepper state (0 to 7)
self.step = 0 #stepper step (up, down)
self.warning = 0 # 0 = ok, 1 warning, 2 error
# bits 0 to 2 are led dirs, bits 4 to 6 are led States
self.charliePlexingState = 0
layoutV = QVBoxLayout()
self.palOn = QPalette()
self.palOn.setColor(QPalette.Base,Qt.darkGreen)
self.palOff = QPalette()
self.palOff.setColor(QPalette.Base,Qt.darkRed)
self.palError = QPalette()
self.palError.setColor(QPalette.Window,Qt.red)
self.palWarning = QPalette()
self.palWarning.setColor(QPalette.Window,Qt.darkRed)
gbox=QGroupBox("user leds")
#Gpio pin state
#led D3
layout = QHBoxLayout()
chkbox_LED_D3=QCheckBox("D3 (PB0)")
chkbox_LED_D3.setCheckable(False)
chkbox_LED_D3.setAutoFillBackground(True)
chkbox_LED_D3.setPalette(self.palOff)
self.leds.append((0,chkbox_LED_D3))
layout.addWidget(chkbox_LED_D3)
#Gpio pin state
#led D13
chkbox_LED_D13=QCheckBox("D13 (PB3)")
chkbox_LED_D13.setCheckable(False)
chkbox_LED_D13.setAutoFillBackground(True)
chkbox_LED_D13.setPalette(self.palOff)
self.leds.append((3,chkbox_LED_D13))
layout.addWidget(chkbox_LED_D13)
gbox.setLayout(layout)
layoutV.addWidget(gbox)
gbox=QGroupBox("push buttons")
#Gpio pin state
#led D3
layout = QHBoxLayout()
chkbox_PB_D5=QCheckBox("D5 (PB6)")
chkbox_PB_D5.setCheckable(True)
self.buttons.append((6,chkbox_PB_D5))
layout.addWidget(chkbox_PB_D5)
#Gpio pin state
#led D13
chkbox_PB_D6=QCheckBox("D6 (PB1)")
chkbox_PB_D6.setCheckable(True)
self.buttons.append((1,chkbox_PB_D6))
layout.addWidget(chkbox_PB_D6)
gbox.setLayout(layout)
layoutV.addWidget(gbox)
for i,pb in self.buttons:
pb.stateChanged.connect(lambda x, i=i: self.buttonChecked(x,i))
#stepper
gbox=QGroupBox("stepper state")
layout = QHBoxLayout()
self.labelStepperState = QLabel('state : 0')
self.labelStepperStep = QLabel('step : 0')
layout.addWidget(self.labelStepperState)
layout.addWidget(self.labelStepperStep)
gbox.setLayout(layout)
layoutV.addWidget(gbox)
#ADC pin state
gbox=QGroupBox("ADC - potentiometer")
layout = QHBoxLayout()
slider = QSlider(Qt.Horizontal)
slider.setMinimum(0)
slider.setMaximum(4095)
layout.addWidget(slider)
self.labelADC = QLabel('0')
slider.valueChanged.connect(self.updateSlider)
layout.addWidget(self.labelADC)
gbox.setLayout(layout)
layoutV.addWidget(gbox)
widget = QWidget()
widget.setLayout(layoutV)
self.setCentralWidget(widget)
def buttonChecked(self,state,pin):
""" called when a checkbox is checked """
gpioId = 1 #2 buttons on GPIOB
#print("send msg: GPIO "+str(gpioId)+", pin "+str(pin)+', state '+str(state==Qt.Checked) )
s=struct.pack("=IIII",pipc_gpio_magic,pin,state==Qt.Checked,gpioId)
mq_to_qemu.send(s)
def updateSlider(self,val):
""" called each time the slider value is updated
"""
self.labelADC.setText(str(val))
#print("send msg to ADC 0:"+str(val))
s=struct.pack("=III",pipc_adc_magic,0,val)
mq_to_qemu.send(s)
def updateStepper(self,output):
steps = [8,0xC,4,6,2,3,1,9]
#print('out: '+str(output))
if(output == steps[self.state]): #nothing
pass
elif(output == steps[(self.state+1)%8]): #+1
self.state = (self.state+1)%8
self.step += 1
elif(output == steps[(self.state+7)%8]): #-1
self.state = (self.state+7)%8
self.step -= 1
else: #pb:
self.warning = 1
i = 0
done = False
for s in steps:
if output == s:
self.state = i
done = True
break
if not done:
self.warning = 2
if self.warning > 0:
#print('warning: '+str(self.warning))
self.labelStepperState.setAutoFillBackground(True)
if self.warning == 1:
self.labelStepperState.setPalette(window.palWarning)
else: #error
self.labelStepperState.setPalette(window.palError)
self.labelStepperState.setText('state : '+str(self.state))
self.labelStepperStep.setText('step : '+str(self.step))
def setGPIO(self,port,dir_mask,output):
#print('gpio '+str(port)+', dir'+str(dir_mask)+', out'+str(output))
if port == 'B': #pin0 is PB7
for (pin,widget) in self.leds:
pinMask = 1<<pin
if (dir_mask & pinMask == pinMask) and (output & pinMask == pinMask):
widget.setPalette(self.palOn)
else:
widget.setPalette(self.palOff)
if port == 'A': #pin0 is PB7
#ok, stepper motor: pin 5 to 8
outputMask = 0xF << 5
if (dir_mask & outputMask) == outputMask:
self.updateStepper((output >> 5) & 0xF)
app = QApplication(sys.argv)
window = MainWindow()
def receiver():
while True:
msg = mq_from_qemu.receive()
mg, changed_out, dir_mask,output,gpio = struct.unpack("=IHHHH",msg[0])
#print("mg=",mg," pin=",pin," gpio=",gpio," state=",state)
if mg != pipc_gpio_magic:
raise Exception("Wrong magic number in GPIO IPC message")
name = ['A','B','C','D','F']
if gpio < 5:
window.setGPIO(name[gpio],dir_mask,output) #TODO: thread safe?
#required if we get too much messages to let time for the UI.
time.sleep(.01)
def warningPersistence():
while True:
time.sleep(2)
if window.warning > 0:
window.warning = 0
window.labelStepperState.setAutoFillBackground(False)
#print('reinit')
window.show()
thread = threading.Thread(target=receiver)
thread.daemon = True
thread.start()
threadWarning = threading.Thread(target=warningPersistence)
threadWarning.daemon = True
threadWarning.start()
app.exec_() #event loop
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment