spokepov.py
来自「旋转16个LED灯控制程序」· Python 代码 · 共 1,102 行 · 第 1/3 页
PY
1,102 行
#!/usr/bin/env python# The software for SpokePOV is available for use in accordance with the # following open source license (MIT License). For more information about# OS licensing, please visit -> http://www.opensource.org/## For more information about SpokePOV, please visit# -> http://www.ladyada.net/make/spokepov## *****# Copyright (c) 2005 Limor Fried## Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions:## The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER# DEALINGS IN THE SOFTWARE.# *****# 06/27/06 - RJW (trebor@animeigo.com) - added ability to import a rectangular# bitmap and conform it to the radial pixels (1 to 1 mapping)# NOTE: does not compute the background image yet!import sys, timeimport parallelimport arrayimport wximport mathimport ConfigParserimport pickle# Note: this is my first Python/wx program. It probably has mistakes, bugs,# bad code. Oh well. -- ladyada# which mode we are when dragging the mouse in the wheel panelDRAWING = 1ERASING = 0PI = 3.1415#default rotationautorotation = 0#default hub diameter is 2" (*4 LEDs per inch)hubsize = 2*4#default LEDs per row is 30, BMX wheels have 22num_leds = 30############## spoke interface commands, must be the same in firmware!COMP_CMD_SETFLED = 0x81COMP_CMD_SETBLED = 0x82COMP_CMD_CLRFLED = 0x83COMP_CMD_CLRBLED = 0x84COMP_CMD_RDEEPROM = 0x85COMP_CMD_WREEPROM = 0x86COMP_CMD_RDEEPROM16 = 0x87COMP_CMD_WREEPROM16 = 0x88COMP_SUCCESS = 0x80############## spokepov parallel port pinsSPI_CLK = 3; # DATA3SPI_DO = 0; # DATA0############## graphics constant: size of the wheel panel. 600x600 seems goodWHEEL_H = 600WHEEL_W = 600# how many radial lines to be swept out per rev, should match firmwareROWS_PER_WHEEL = 256 # variables stored in the microcontrollerEEPROM_ROTOFFSET = 0x8000EEPROM_MIRROR = 0x8001####################################################3# this class talks to the actual spokes########class SpokePOVComm: def __init__(self): self.p = parallel.Parallel() # open LPT1 self.out(0xFF) # if there's a spokepov on the other side, this pin should be low def connected(self): r = ~ self.p.getInAcknowledge() return r # since theres only a byte-wide byte output function, these functions # allow setting and clearing individual pins def out(self, b): self.data = b self.p.setData(self.data) def setbit(self, b): self.data |= 1 << b self.p.setData(self.data) def clrbit(self, b): self.data &= ~(1 << b) self.p.setData(self.data) # bit-bang the SPI protocol over to the microcontroller def spi_xfer(self, c): #print "sending %02x\n" % c x = 7 ret = 0 while x >= 0: if (self.connected() == 0): raise IOError, "not connected" # send data out if c & (1 << x): self.setbit(SPI_DO) else: self.clrbit(SPI_DO) self.setbit(SPI_CLK) #print "^" #ya = raw_input() time.sleep(0.001) # get data in ret <<= 1 ret |= self.p.getInBusy() self.clrbit(SPI_CLK) time.sleep(0.001) #print "v" #ya = raw_input() x -= 1 #print "got %02x" % ret return ret # read one byte from the external EEPROM def read_eeprom(self, addr): self.spi_xfer(COMP_CMD_RDEEPROM) #time.sleep(0.01) self.spi_xfer((addr >> 8) & 0xFF) #time.sleep(0.01) self.spi_xfer(addr & 0xFF) #time.sleep(0.01) val = self.spi_xfer(0) #time.sleep(0.01) ret = self.spi_xfer(0); if (ret != 0x80): print "failed! 0x%02x" % ret raise IOError, "Didn't succeed" return val # read 16 bytes from the external EEPROM (faster!) def read_eeprom16(self, addr): self.spi_xfer(COMP_CMD_RDEEPROM16) self.spi_xfer((addr >> 8) & 0xFF) self.spi_xfer(addr & 0xFF) x = 16 val = [] while (x != 0): val.append(self.spi_xfer(0)) x -= 1 ret = self.spi_xfer(0); if (ret != 0x80): raise IOError, "Didn't succeed" return val def write_eeprom(self, addr, val): self.spi_xfer(COMP_CMD_WREEPROM) self.spi_xfer((addr >> 8) & 0xFF) self.spi_xfer(addr & 0xFF) self.spi_xfer(val) ret = self.spi_xfer(0); if (ret != 0x80): raise IOError, "Didn't succeed" def write_eeprom16(self, addr, val): self.spi_xfer(COMP_CMD_WREEPROM16) self.spi_xfer((addr >> 8) & 0xFF) self.spi_xfer(addr & 0xFF) x = 0 while (x != 16): self.spi_xfer(val[x]) x += 1 ret = self.spi_xfer(0); if (ret != 0x80): raise IOError, "Didn't succeed" def sendcmd(self, cmd, arg): self.spi_xfer(cmd) time.sleep(0.005) self.spi_xfer(arg) time.sleep(0.005) ret = self.spi_xfer(0); if (ret != 0x80): raise IOError, "Didn't succeed"# This model is like an array, except I couldnt figure out how to get# arrays to do what I want. This also keeps track of whether its been# modified, which speeds up drawingclass WheelModel: def __init__(self): self.arr = [None]*ROWS_PER_WHEEL self.row = [None]*num_leds def __getitem__(self, (i, j)): return (self.arr[i] or self.row)[j] def __setitem__(self, (i, j), value): if self.arr[i]==None: self.arr[i] = self.row[:] self.arr[i][j] = value self.changed = 1 def has_changed(self): return self.changed def set_changed(self, f): self.changed = f def clone(self): c = WheelModel() for i in range(0,num_leds): for j in range(0,ROWS_PER_WHEEL): c[(i, j)] = self[(i,j)] return c# The model for the datamodel = WheelModel()######################################### the panel widget that deals with clicks and drawing & stuff#############class WheelPanel(wx.Panel): image = None def __init__(self, parent): global model wx.Panel.__init__(self, parent) # initialize the model for x in range(0 , ROWS_PER_WHEEL): for y in range(0 , num_leds): model[(x, y)] = 0 (width, height) = self.GetSizeTuple() self._BackgroundBuffer = None self._ForegroundMaskBuffer = None # the red buffer is a large red square which is then # masked by the pixel mask, which makes it much faster to # draw and erase the images. self.RedBuffer = wx.EmptyBitmap(width, height) reddc = wx.MemoryDC() reddc.SelectObject(self.RedBuffer) reddc.SetPen(wx.RED_PEN) reddc.SetBrush(wx.RED_BRUSH) reddc.DrawRectangle(0, 0, width, height) reddc.SelectObject(wx.NullBitmap) self.mask = None self.SetBackgroundColour(wx.NamedColour('white')) self.Bind(wx.EVT_PAINT, self.OnPaint) self.SetFocus() self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) self.Bind(wx.EVT_MOTION, self.OnMotion) self.drawstate = None def OnSize(self, event): self._BackgroundBuffer = wx.EmptyBitmap(self.Width, self.Height) self._ForegroundMaskBuffer = wx.EmptyBitmap(self.Width, self.Height) self.UpdateBackground() self.Refresh() # called when the model changed and the foreground mask must be recalculated def UpdateForegroundMask(self): global model, hubsize (width, height) = self.GetSizeTuple() self._ForegroundMaskBuffer = wx.EmptyBitmap(width, height) dc = wx.MemoryDC() dc.SelectObject(self._ForegroundMaskBuffer) dc.Clear(); middle = wx.Point(width/2, height/2) dDiameter = min(width, height) / (num_leds + hubsize) # draw the pixels pen = wx.Pen("green", dDiameter/2, wx.SOLID) pen.SetCap(wx.CAP_BUTT) dc.SetPen(pen) dc.SetBrush(wx.TRANSPARENT_BRUSH) dAngle = 360.0/ROWS_PER_WHEEL for j in range(0,num_leds): endangle = startangle = -1 diam = (j+.5+hubsize) * dDiameter for i in range(0, ROWS_PER_WHEEL): if (model[(i,j)] == 0): if (endangle == -1): continue # draw this sweep #print (startangle, endangle) dc.DrawEllipticArc((middle.x - (diam/2) + .5), (middle.y - (diam/2) + .5), diam, diam, startangle, endangle) startangle = endangle = -1 if (model[(i,j)] == 1): if (endangle == -1): endangle = 90.0 - (i*360.0/ROWS_PER_WHEEL) startangle = endangle startangle -= dAngle if (startangle != -1): dc.DrawEllipticArc((middle.x - (diam/2) + .5), (middle.y - (diam/2) + .5), diam, diam, startangle, endangle) dc.SelectObject(wx.NullBitmap) # call this when you want to draw the window from scratch # (and update the double buffer) # ie when a new image is imported def UpdateBackground(self): global hubsize # print "bg update" (width, height) = self.GetSizeTuple() self.RedBuffer = wx.EmptyBitmap(width, height)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?