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 + -
显示快捷键?