safe round buffer implementation

This commit is contained in:
Nikolay Khabarov
2017-07-01 14:27:37 +03:00
parent e441b5dc1f
commit c517044316
3 changed files with 63 additions and 21 deletions
+31 -8
View File
@@ -175,17 +175,32 @@ def move(generator):
""" Move head to specified position
:param generator: PulseGenerator object.
"""
# wait if previous command still works
while dma.is_active():
time.sleep(0.001)
# Fill buffer right before currently running(previous sequence) dma
# this mode implements kind of round buffer, but protects if CPU is not
# powerful enough to calculate buffer in advance, faster then machine
# moving. In this case machine would safely paused between commands until
# calculation is done.
# 4 control blocks per 32 bytes
bytes_per_iter = 4 * dma.control_block_size()
# prepare and run dma
dma.clear()
dma.clear() # should just clear current address, but not stop current DMA
prev = 0
is_ran = False
instant = INSTANT_RUN
st = time.time()
current_cb = 0
k = 0
k0 = 0
for direction, tx, ty, tz, te in generator:
if current_cb is not None:
while dma.current_address() + bytes_per_iter >= current_cb:
time.sleep(0.001)
current_cb = dma.current_control_block()
if current_cb is None:
k0 = k
st = time.time()
break # previous dma sequence has stopped
if direction: # set up directions
pins_to_set = 0
pins_to_clear = 0
@@ -229,19 +244,27 @@ def move(generator):
# matter for pulses with 1-2us length.
prev = k + STEPPER_PULSE_LENGTH_US
# instant run handling
if not is_ran and instant:
if k > 500000: # wait at least 500 ms is uploaded
if time.time() - st > 0.5:
if not is_ran and instant and current_cb is None:
if k - k0 > 100000: # wait at least 100 ms is uploaded
nt = time.time() - st
ng = (k - k0)/ 1000000.0
if nt > ng:
logging.warn("Buffer preparing for instant run took more "
"time then buffer time")
"time then buffer time"
" {}/{}".format(nt, ng))
instant = False
else:
dma.run_stream()
is_ran = True
pt = time.time()
if not is_ran:
# after long command, we can fill short buffer, that why we may need to
# wait until long command finishes
while dma.is_active():
time.sleep(0.01)
dma.run(False)
else:
# stream mode can be activated only if previous command was finished.
dma.finalize_stream()
logging.info("prepared in " + str(round(pt - st, 2)) + "s, estimated in "
+12
View File
@@ -241,6 +241,18 @@ class DMAGPIO(DMAProto):
"""
self.__current_address = 0
def current_address(self):
""" Get current buffer offset.
:return: current buffer offset in bytes.
"""
return self.__current_address
def control_block_size(self):
""" Get control block size.
:return: control block size in bytes.
"""
return self._DMA_CONTROL_BLOCK_SIZE
class DMAPWM(DMAProto):
_DMA_CONTROL_BLOCK_SIZE = 32
+20 -13
View File
@@ -198,7 +198,7 @@ class DMAProto(object):
""" This class provides basic access to DMA and creates buffer for
control blocks.
"""
self._DMA_CHANNEL = dma_channel
self._DMA_CHANNEL_ADDRESS = 0x100 * dma_channel
# allocate buffer for control blocks
self._phys_memory = CMAPhysicalMemory(memory_size)
# prepare dma registers memory map
@@ -207,34 +207,41 @@ class DMAProto(object):
def _run_dma(self):
""" Run DMA module from created buffer.
"""
address = 0x100 * self._DMA_CHANNEL
self._dma.write_int(address + DMA_CS, DMA_CS_END)
self._dma.write_int(address + DMA_CONBLK_AD,
self._dma.write_int(self._DMA_CHANNEL_ADDRESS + DMA_CS, DMA_CS_END)
self._dma.write_int(self._DMA_CHANNEL_ADDRESS + DMA_CONBLK_AD,
self._phys_memory.get_bus_address())
cs = DMA_CS_PRIORITY(7) | DMA_CS_PANIC_PRIORITY(7) | DMA_CS_DISDEBUG
self._dma.write_int(address + DMA_CS, cs)
self._dma.write_int(self._DMA_CHANNEL_ADDRESS + DMA_CS, cs)
cs |= DMA_CS_ACTIVE
self._dma.write_int(address + DMA_CS, cs)
self._dma.write_int(self._DMA_CHANNEL_ADDRESS + DMA_CS, cs)
def _stop_dma(self):
""" Stop DMA
"""
address = 0x100 * self._DMA_CHANNEL
cs = self._dma.read_int(address + DMA_CS)
cs = self._dma.read_int(self._DMA_CHANNEL_ADDRESS + DMA_CS)
cs |= DMA_CS_ABORT
self._dma.write_int(address + DMA_CS, cs)
self._dma.write_int(self._DMA_CHANNEL_ADDRESS + DMA_CS, cs)
cs &= ~DMA_CS_ACTIVE
self._dma.write_int(address + DMA_CS, cs)
self._dma.write_int(self._DMA_CHANNEL_ADDRESS + DMA_CS, cs)
cs |= DMA_CS_RESET
self._dma.write_int(address + DMA_CS, cs)
self._dma.write_int(self._DMA_CHANNEL_ADDRESS + DMA_CS, cs)
def is_active(self):
""" Check if DMA is working. Method can check if single sequence
still active or cycle sequence is working.
:return: boolean value
"""
address = 0x100 * self._DMA_CHANNEL
cs = self._dma.read_int(address + DMA_CS)
cs = self._dma.read_int(self._DMA_CHANNEL_ADDRESS + DMA_CS)
if cs & DMA_CS_ACTIVE == DMA_CS_ACTIVE:
return True
return False
def current_control_block(self):
""" Get current dma control block address.
:return: Currently running DMA control block offset in bytes or None
value if DMA is not running.
"""
cb = self._dma.read_int(self._DMA_CHANNEL_ADDRESS + DMA_CONBLK_AD)
if cb == 0:
return None
return cb - self._phys_memory.get_bus_address()