Sunday, January 29, 2017


Raspberry - PI 
 Analogue  / PWM / DIO/ DC motor / Stepper  
I2C Expansion  Board 
also for:
other R-PI replacements  
Arduino* , 
ESP8266* 
and 
Linux PC's via VGA port
(* Only I2C access guaranteed no Python direct portability)



  All the new and on trend hobbyist's microcomputers available on the market have a limited number of I/O available inherently by the nature of the hardware and very few have analog inputs.
  The current proposal is describing an elegant possibility to expand the periphery of a such device by using a Microchip IC , the case described being  PIC18F46K80 which offers a large range of different applications.
  This PIC has a lot of features many of them beyond the needs or targets for a peripheral extension for R-PI or Arduino.

  Project the targets :
  1. Generate an I2C slave device with programmable I2C address (1 to 127). 
  2. Build an I2C extension for Analog and digital inputs (I2C Sensor Core).
  3. Build an I2C extension for Digital Outputs including PWM outputs.
  4. Expand the PWM and DIO to defined DC Motor objects 
  5. Expand PWM outputs for Stepper motor command mono and bipolar
  6. Stream Serial Data with  RS232  in text mode (for non Linux devices)
  7. Include a frequency counter. (not yet)
The main electronic component used in the project is  PIC 18F46K80  produced by Microchip:
http://www.microchip.com/wwwproducts/en/PIC18F46K80

---------------------The peripheral highlights ---------------------

Five CCP/ECCP modules:
  •  Four Capture/Compare/PWM (CCP) modules
  •  One Enhanced Capture/Compare/PWM(ECCP) modul
Five 8/16-Bit Timer/Counter modules:
  • Timer0: 8/16-bit timer/counter with 8-bitprogrammable prescaler
  • Timer1, Timer3: 16-bit timer/counter
  • Timer2, Timer4: 8-bit timer/counter
Two Analog Comparators
Configurable Reference Clock Output
Charge Time Measurement Unit (CTMU):
  •  Capacitance measurement
  •  Time measurement with 1 ns typical resolution
  •  Integrated voltage reference
High-Current Sink/Source 25 mA/25 mA(PORTB and PORTC)Up to Four External Interrupts
One Master Synchronous Serial Port
(MSSP) module:
  • 3/4-wire SPI (supports all four SPI modes)
  • I2C TM Master and Slave modes
Two Enhanced Addressable USART modules:
  • LIN/J2602 support
  • Auto-Baud Detect (ABD)
12-Bit A/D Converter with up to 11 Channels:
  • Auto-acquisition and Sleep operation
  • Differential Input mode of operation
Data Signal Modulator module:
  • Select modulator and carrier sources from various module outputs
  • Integrated Voltage Reference
--------------------------------------------------------------------

This project is using a 40  pin DIP circuit with the pin map as follows:
----------------------------------------------------------
The I2C_Core adds via I2C 11 (12 bit) A/D (AN) and 9 DIO + 5 PWM sources. The pin association  is as follows:
This is a 'Solid Map " in the development pin definitions can't be changed (easy..)

Typical Test Board setup with PIC18F46K80

Board Connected to Host via I2C protocol

Green - SDA, Yellow - SCL, Black - GND

Basic features callable from  master device with Python 2.7 

  The  assumption is that you can or want to read the Python code to be able to use the functions described bellow.  
 During the development of the Python source (2.7 Spyder implementation ) the outcome increased in complexity defining classes and objects

  Most of the reading functions (Read or Get) will return a tuple containing  a range of information regarding the operation. 

  Read the code to figure out which part you want to use.

  The Python version used is 2.7X (not 3)
  The code was developed under the (free) Anaconda / Spyder  IDE but works also under IDLE 2.7  or under standard Python 2.7  language opened in a terminal. 

  Spyder and IDLE are  also available under Windows but in my experience I2C continues to be difficult to access in a cheap way under Windows , and Linux is anyway the more elegant and free environment. I don't think installing a Linux virtual box under W will help.
  1. The Main file is called Core15.py 
  2. The main program file defines  usable objects which interact with the PIC hardware.
  3. The PIC18746K80 was programmed under MicroPascal using PICKit3 as programming interface thru MpLab .
  4. The source for PIC will be described in an future post.
  5. The Python Anaconda Spyder environment is a fully loaded Python and most of the precious Python modules are already available.
The modules which have to be loaded at the start : (which you can find that need other modules so please load your Python...:)

import smbus   - is the module dealing with the I2C network and communication
from time import * - Time measuring procedures 
from matplotlib.pyplot  plot as plt - graphical representations
from numpy import * - main mat module
import os  - system functions (save files dir etc)
from smoothing import * - this is a file for smoothing the recorded data , I did adapt it from existing files without changes major changes. This is an external file which has to be present in the working folder.
For the original posts please see:
https://stackoverflow.com/questions/20618804/how-to-smooth-a-curve-in-the-right-way
=====================================================================
https://stackoverflow.com/questions/20618804/how-to-smooth-a-curve-in-the-right-way

=====================================================================
def savitzky_golay(y, window_size, order, deriv=0, rate=1):
    import numpy as np
    from math import factorial
    try:
        window_size = np.abs(np.int(window_size))
        order = np.abs(np.int(order))
    except ValueError, msg:
        raise ValueError("window_size and order have to be of type int")
    if window_size % 2 != 1 or window_size < 1:
        raise TypeError("window_size size must be a positive odd number")
    if window_size < order + 2:
        raise TypeError("window_size is too small for the polynomials order")
    order_range = range(order+1)
    half_window = (window_size -1) // 2
    # precompute coefficients
    b = np.mat([[k**i for i in order_range] for k in range(-half_window, half_window+1)])
    m = np.linalg.pinv(b).A[deriv] * rate**deriv * factorial(deriv)
    # pad the signal at the extremes with
    # values taken from the signal itself
    firstvals = y[0] - np.abs( y[1:half_window+1][::-1] - y[0] )
    lastvals = y[-1] + np.abs(y[-half_window-1:-1][::-1] - y[-1])
    y = np.concatenate((firstvals, y, lastvals))
    return np.convolve( m[::-1], y, mode='valid')
   
def smooth(y, box_pts):
    box = np.ones(box_pts)/box_pts
    y_smooth = np.convolve(y, box, mode='same')
    return y_smooth

=====================================================================
   The Core15 has a defined function named aide()  and the call of it will display a text version of the help which includes a simple schematic of the PIC connections.
In fact is displaying a multi line string  called hlp.

hlp="""
*******************************************************************************
I2C CORE EXPANDER
*******************************************************************************
Created  2017
*******************************************************************************
@ author: Calin Raszga (SR) / CR2875 @
*******************************************************************************
Python interface for PIC I2C CORE expander - Pin Allocation:
*******************************************************************************
Supply +5V (3.3 possible)
-------------------------------------------------------------------------------
--Pin and I/O allocation PIC18F46K80
--4.7 K pullup rezistors needed fot SLC and SDA pins. [SDA,SLC to(+)trhu 4.7k]
-- Test first without, SDA SCL are pulled up
-------------------------------------------------------------------------------
MCLR            1-#######-40    B7    PGD
AN0---    A0    2-#######-39    B6    PGC
AN1---    A1    3-#######-38    B5    PWM5
AN2---    A2    4-#######-37    B4    AN9
AN3---    A2    5-#######-36    B3    DIO_11
3.3 V           6-#######-35    B2    DIO_10
AN4---    A5    7-#######-34    B1    ---AN8
AN5---    E0    8-#######-33    B0    ---AN10
AN6---    E1    9-#######-32    (+)    5V
AN7---    E2   10-# PIC #-31    (-)     
5V       (+)   11-# 18F #-30    D7    RX2
         (-)   12-# 46  #-29    D6    TX2
DIO_0    A7    13-# K80 #-28    D5    DIO_9
DIO_1    A6    14-#######-27    D4    PWM1
DIO_2    C0    15-#######-26    C7    PWM4
DIO_3    C1    16-#######-25    C6    PWM3
PWM2     C2    17-#######-24    C5    DIO_8   Reset Core  address to 0X30
I2C-SCL  C3    18-#######-23    C4    I2C-SDA
DIO_4    D0    19-#######-22    D3    DIO__7
DIO_5    D1    20-#######-21    D2    DIO__6
-------------------------------------------------------------------------------
---------------Motor commands--------------------------------------------------
    PWM     DIO     Action 
    0-255   1       CW (0 to max speed) - Motor.CW
    0-255   0       CCW (0 to max speed)- Motor.CCW
    0       0       FREE                - Motor.FRE
    255     1       BRAKE               - Motor.BRK
-------------------------------------------------------------------------------
Bipolar PIC_Stepper
W1 - DIO_4 (D0)
W2
W3
W4 - DIO_7 (D3)
-------------------------------------------------------------------------------
Notes regarding the use of the programmed structure
The default structure is based on the self init commands:

C,AN,D,PW,M,E,STPR,PW_STPR=Auto_Core()[0:8]
C - Core Class - those are setings for the PIC I2C address 

The next ones are refferintg the PIC pins above
AN- Analogues definition
D- Digital IO definition
PW- PWM active pins om PIC
M - DC motor
E - EEProm read / write on pic

This version stops inspecting networks if finds the forst net I2C with
the CIP signature
-------------------------------------------------------------------------------

"""

In the main source of your program loading the Core15 is the first  step , usually if you have only one CORE (piece of hardware) I would do :

from Core15 import *

This will start (launch)  the initialization program. Because all networks and probable addresses are tested it can take a bit longer if you connect from a computer with a few I2C networks embedded in the Video Card usually.

The procedures doesn't necessarily look for video card but for the presence of the I2C networks and I2C peripherals  on them.
R-PI has its own I2C integrated in the hardware so it will be detected as any other systems.

For the speed of development and for the comodity of the hardware I developed the application on an Intel Pentium system running a Debian 8 (Jessie) Linux distribution.
Under Python the platform module will give you info regarding your machine.
-------------------------------------------------------
>>> platform.architecture()
('64bit', 'ELF')
>>> platform.processor()
''
>>> platform.platform(aliased=0, terse=0)
'Linux-3.16.0-4-amd64-x86_64-with-debian-8.10'
>>>
---------------------------------------------------

 The VGA / I2C network present for free in our desktops deserves a  mention here because it is a fasrt and cheap way by using an older computer to do your developements much faster than by using  R-PI or an other similar one.
 Your final application can be run on the small one but the development can be done for free and fast on a decent desktop 10 Y old... which probable will have a 100 GH HDD and 4 GB or more Ram which will make your 64bit distribution to fly...

***************VGA I2C *********************************************************
The I2C PC network from VGA: (accessible under Linux !)
- You can ignore the next section if you use a R-PI 2 as there the I2C is available on the 40 pin connector.(SDA , SCL)
- The VGA port has a native I2C connection available.
- The easiest way to access it is if your card has a dual port , by attaching an VGA extension cable, or eventually connecting directly in the card connector "holes".This will work with an laptop by connecting to the extension VGA connector or HDMI
- If you use a modified video cable check if it hass all the pin connections to the card connector.
- You can gather the network from a Flat Screen monitor by connecting to the unused DVI or VGA port. ...

Connector pins:


- The easy way to identify the pins is by looking for the 5V pin with a voltmeter. This will place you on the "map". In fact in my application I'm connecting to a DVI port with an adapter to VGA and then I'm connecting the extension cable (from an old KVM switch).
==========================================================
How to check your I2C network (Debian 8 and R-PI):
- The i2c i module is accessible only in Root mode (which is default in R-Pi) but you need to  work in Root  mode for other computers (PC) under Debian  (at least per my limited experience)
- launch terminal
su
modprobe i2c-dev      
i2cdetect -l
// (to list all networks)




- if you have an I2C device attached on the network you should see it on one of the networks with
i2cdetect -y 0
//(or 1 or 2 or 3 .. usually I find   0 or 1  hosting the devices).
Note: it is possible that a computer reset or shoot down the devices will be on a different net as I learned.


The devices for which I'm looking have addresses 0X40 , 0X48 , 0X68, 0X69 , so is the I2C number 1
******************************************************************************
  The program is looking on the networks for the typical identification of the CORE hardware :

(Created but not set use .test or .set)
Bus 0  exists ======================
Addr exists at  0x38
Addr exists at  0x4c
Bus 1  exists ======================
Addr exists at  0x30
**PIC on bus: 1 address: 0x30 Signature: >>CIP

The system identified the existence of the hardware at 0X30 by the signature >>CIP.

Also it will define all the objects related to the CORE by bus number and hex address:
ADC  1 0x30 -  Analogue input
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
ADC  1 0x30
DIO  1 0x30- Didgital I/O input output (pending on setting)
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
DIO  1 0x30
PWM  1 0x30 - PWM signal source 
PWM  1 0x30
PWM  1 0x30
PWM  1 0x30
PWM  1 0x30
Motor  1 0x30 - DC motor signal combination (PWM + DIO)
Motor  1 0x30
Motor  1 0x30
Motor  1 0x30
Motor  1 0x30
EEPROM 1 0x30 - PIC internal EEPROM 
PIC BiPol Stepper  1 0x30 -  stepper motor signal combination DIO
PIC BiPol PWM Stepper  1 0x30 --  stepper motor signal combination PWM



  1. The I2C_CORE address can be set with the Pic_Addr_Change  function. The new address will take effect after Reset.
  2. The Basic Address 0X30 (48) can be reset by pressing Reset and DIO_8=0 (C5=0) (connect to GND). Release Reset and then DIO_8. Reset Again and your default address is again 0X30 (48)
  3. If the I2C net is available and the I2C_CORE in on it the (test_I2C_address()) function is scanning all buses and networks and will identify the first  I2C_Core based on a key response.
  4. In all functions the address of the I2C_CORE is mentioned so that you can use more than 1 core simultaneous. (Extended version in work)
  5. Further development will simplify the interaction by objectifying the concept.
  6. Read Analog  inputs (ADC_R(address,i):) 0 to 9 with 12 bit conversion (0-4095) 0-5 V range  
  7. Reading 10 Digital I/O (DIO)(Read_DIO(address,i):)   will return the stage of the DIO pin (0/1)
  8. Set DIO to  0 or 1 with (Set_DIO(adr,DIO_N,Stage)) where Stage is 0 for input, 1 for output=1 and 2 for output=1.
  9. The 5 PWM's available , set at 3000 HZ can be set with (Set_PWM(adr,PWM_NR,duty)).





The Python code for the RIP or another I2C capable Linux box , built as a self configuring class:


# -*- coding: utf-8 -*-

hlp="""
*******************************************************************************
I2C CORE EXPANDER
*******************************************************************************
Created  2017
*******************************************************************************
@ author: Calin Raszga (SR) / CR2875 @
*******************************************************************************
Python interface for PIC I2C CORE expander - Pin Allocation:
*******************************************************************************
Supply +5V (3.3 possible)
-------------------------------------------------------------------------------
--Pin and I/O allocation PIC18F46K80
--4.7 K pullup rezistors needed fot SLC and SDA pins. [SDA,SLC to(+)trhu 4.7k]
-- Test first without, SDA SCL are pulled up
-------------------------------------------------------------------------------
MCLR            1-#######-40    B7    PGD
AN0---    A0    2-#######-39    B6    PGC
AN1---    A1    3-#######-38    B5    PWM5
AN2---    A2    4-#######-37    B4    AN9
AN3---    A2    5-#######-36    B3    DIO_11
3.3 V           6-#######-35    B2    DIO_10
AN4---    A5    7-#######-34    B1    ---AN8
AN5---    E0    8-#######-33    B0    ---AN10
AN6---    E1    9-#######-32    (+)    5V
AN7---    E2   10-# PIC #-31    (-)     
5V       (+)   11-# 18F #-30    D7    RX2
         (-)   12-# 46  #-29    D6    TX2
DIO_0    A7    13-# K80 #-28    D5    DIO_9
DIO_1    A6    14-#######-27    D4    PWM1
DIO_2    C0    15-#######-26    C7    PWM4
DIO_3    C1    16-#######-25    C6    PWM3
PWM2     C2    17-#######-24    C5    DIO_8   Reset Core  address to 0X30
I2C-SCL  C3    18-#######-23    C4    I2C-SDA
DIO_4    D0    19-#######-22    D3    DIO__7
DIO_5    D1    20-#######-21    D2    DIO__6
-------------------------------------------------------------------------------
---------------Motor commands--------------------------------------------------
    PWM     DIO     Action 
    0-255   1       CW (0 to max speed) - Motor.CW
    0-255   0       CCW (0 to max speed)- Motor.CCW
    0       0       FREE                - Motor.FRE
    255     1       BRAKE               - Motor.BRK
-------------------------------------------------------------------------------
Bipolar PIC_Stepper
W1 - DIO_4 (D0)
W2
W3
W4 - DIO_7 (D3)
-------------------------------------------------------------------------------
Notes regarding the use of the programmed structure
The default structure is based on the self init commands:
C,AN,D,PW,M,E,STPR,PW_STPR=Auto_Core()[0:8]
C - Core Class - those are setings for the PIC I2C address 
The next ones are refferintg the PIC pins above
AN- Analogues definition
D- Digital IO definition
PW- PWM active pins om PIC
M - DC motor
E - EEProm read / write on pic

-------------------------------------------------------------------------------
"""
#!/usr/bin/python
# Program to use the PIC18F46K80 thru I2C commnication
#---------Import modules--------------------------------
import smbus
from time import *
from matplotlib.pyplot import plot,hist,grid
from numpy import *
import os
from smoothing import *
#import serial
os.chdir('/home/cr/Documents/PI18_I2C/')
#==============================================================================

#--------------------------Global function for help----------------------------
def aide():
    print hlp
#------------------------------------------------------------------------------
#******************************************************************************
#******************************************************************************       
#**********************Define the I2C core ************************************
class CORE(object):
    def __init__(self):
        print '(Created but not set use .test or .set)'
        #self.test() # self init
#******************************************************************************
    def test(self):    
        busPIC=None
        addPIC=None
        for bs in range (0,0xFF):# all posible buses
                try: # look for existing I2C SMBUS
                        bus=smbus.SMBus(bs)
                        print 'Bus',bs,' exists ======================'
                        for add in range(0,0xFF>>1):# all posible adresssess
                                try:
                                        raw=bus.read_byte(add)
                                        print 'Addr exists at ',hex(add)
                                        s='>>'
                                        for l in range(96,99):
                                                s+=chr(bus.read_byte_data(add,l))
                                        if s=='>>CIP':
                                                print '**PIC on bus:', bs, 
                                                print 'address:',hex(add),
                                                print 'Signature:',s
                                                busPIC=bs
                                                addPIC=add
                                except:         
                                        pass
                except:
                        pass
        self.bus=smbus.SMBus(busPIC)
        self.adr=addPIC
        self.bus_n=busPIC
        return self.bus_n,self.bus,self.adr,hex(self.adr)        
        
    def set(self,bus_n,adr):
        self.bus=smbus.SMBus(bus_n)
        self.adr=adr
        self.bus_n=bus_n
        return self.bus_n,self.bus,self.adr,hex(self.adr)
          
    def Addr_Change(self,a):
        try:
            self.bus.write_byte_data(self.adr,1,a)
            print '*** NEW  PIC address after reset: ',hex(a)
            print ' Reset with DIO_8=0 for standard address 0X30'
        except:
            print 'Address not changed'

#******************************************************************************
# Super class definition 

#==============================================================================
class EEPROM(object):
    
    def __init__(self,CORE):
        print 'EEPROM',CORE.bus_n,hex(CORE.adr)
        self.bus=CORE.bus
        self.adr=CORE.adr

    def Write(self,EEP_adr,value):# EEp_arr 0..23
        ''' 
        There are  24 EEPROM addresses to write or read 
        '''
        if EEP_adr in range(24):        
            self.bus.write_byte_data(self.adr,EEP_adr+9,value)    
        else:
            value='EEPROM address has to be 0 to 23'
        return EEP_adr,value    

    def Read(self,EEP_adr):
        if EEP_adr in range(24):
            value=self.bus.read_byte_data(self.adr,EEP_adr+49)
        else:
            value='EEPROM address has to be in range 0 to 23'
        return EEP_adr, value

#=============================================================================
# Clases declaration starts Here
#******************************************************************************
#******************************************************************************
#******************************************************************************
class ADC(object):
    def __init__(self,CORE,chn):
        self.chn=chn
        print 'ADC ',CORE.bus_n,hex(CORE.adr)
        self.bus=CORE.bus
        self.adr=CORE.adr
#******************************************************************************
    def Read(self,DT=0):
            sleep(DT)
            L=self.bus.read_byte_data(self.adr,self.chn)
            sleep(DT)
            H=self.bus.read_byte_data(self.adr,self.chn+32)
            N=L+256*H
            V=5*N/4095.0
            # N is the numarical value
            # V is 0-5 V equivalent
            return self.chn,N,V

    def Read_Stat(self,pct=10,PL='',DT=0.00):
        # pct - number of measured points
        #PL =Y for plot
        # Returns nuber of points and average and the raw values
        adc=[]
        for i in range(pct):            
            adc.append(self.Read(DT)[2])      
        if PL=='Y':             
            grid()
            plot(adc)   
        # mean, average
        # std standard deviation
        # var variance
        # ptp peak to peak range
        return  pct,mean(adc),average(adc),ptp(adc),var(adc),std(adc),adc

    def Read_Smth(self,pct=100,W=13,OR=4,PL='YF',DT=0.00):
        # pct - number of measured points
        #PL =Y for plot
        # Returns nuber of points and average and the raw values
        adc=[]
        for i in range(pct):            
            adc.append(self.Read(DT)[2])      
        adc_smth=asarray(adc)
        adc_smth=savitzky_golay(adc_smth, W, OR)   
        if PL=='Y':             
            grid()
            plot(adc)
            plot(adc_smth)           
        if PL=='YF':
            grid()
            plot(adc_smth)
        # mean, average
        # std standard deviation
        # var variance
        # ptp peak to peak range
        return  pct,mean(adc),average(adc),ptp(adc),var(adc),std(adc),adc_smth 
             
    def lst(self,i1,i2):
            i0=self.chn
            R=[]
            if i2>11:
                    i2=11
            if i1<0:
                    i1=0;
            for i in range(i1,i2):
                    self.chn=i
                    R.append(self.Read()[1])
            self.chn=i0
            return R
        
    def Plt(self,pct):
            t=[]
            adc=[]
            k=0;
            k0=clock()
            for i in range(pct):
                  t.append(clock()-k0)
                  adc.append(self.Read()[2])
            grid()
            plot(t,adc)
            return max(t),i 
            
    def Rec(self,pct=100,F='F'):
            t0=tf=0
            adc=[]
            t0=clock()
            for i in range(pct):
                adc.append(self.Read()[1])
            tf=clock()-t0
            
            if F=='F':
                adc=asarray(adc)                
                adc=savitzky_golay(adc,17,0)
            return tf,adc 

    def Get_Duty(self,pct):
            vup=0
            for i in range (pct):
                vup+=(self.Read()[1]<1024)
            vup=pct-vup
            return (255*vup)/pct ,255-(255*vup)/pct
    
    def Hist(self,pct,nb):
            x=[]
            x=self.Rec(pct)[1]
            n, bins,patches  =hist(x,nb,range=[0,4095], normed=0, facecolor='green', alpha=0.75)
            grid(True)
            return average(x),n,bins
#******************************************************************************
#******************************************************************************            
#******************************************************************************
class D_IO(object):
    def __init__(self,CORE,DIO_N):
        print 'DIO ',CORE.bus_n,hex(CORE.adr)
        self.bus=CORE.bus
        self.adr=CORE.adr
        self.DIO_N=DIO_N
        self.DIO_Stg=0
        self.DIO=((12,'A7'),(13,'A6'),(14,'C0'),
                  (15,'C1'),(16,'D0'),(17,'D1'),
                  (18,'D2'), (19,'D3'),(20,'C5'),
                  (21,'D5'),(22,'B2'),(23,'B3'))
#******************************************************************************    
    def Read(self):
        sleep(0.001)
        self.DIO_Stg=self.bus.read_byte_data(self.adr,self.DIO[self.DIO_N][0])
        
        return self.DIO_N,self.DIO[self.DIO_N][0],self.DIO[self.DIO_N][1],self.DIO_Stg   
    
    def Set(self,stg):
        self.DIO_Stg=stg
        if self.DIO_Stg<0 or self.DIO_Stg>2:
            self.DIO_Stg=0
            print 'Error',self.DIO_Stg
        Data=self.DIO_N*3+self.DIO_Stg
        self.bus.write_byte_data(self.adr,2,Data)

    def ON(self):
        self.DIO_Stg=2
        if self.DIO_Stg<0 or self.DIO_Stg>2:
            self.DIO_Stg=0
            print 'Error',self.DIO_Stg
        Data=self.DIO_N*3+self.DIO_Stg
        self.bus.write_byte_data(self.adr,2,Data)
        
    def OFF(self):
        self.DIO_Stg=1
        if self.DIO_Stg<0 or self.DIO_Stg>2:
            self.DIO_Stg=0
            print 'Error',self.DIO_Stg
        Data=self.DIO_N*3+self.DIO_Stg
        self.bus.write_byte_data(self.adr,2,Data)

    def INPT(self):
        self.DIO_Stg=0
        if self.DIO_Stg<0 or self.DIO_Stg>2:
            self.DIO_Stg=0
            print 'Error',self.DIO_Stg
        Data=self.DIO_N*3+self.DIO_Stg
        self.bus.write_byte_data(self.adr,2,Data)

    def lst(self,i1,i2):
        I0=self.DIO_N
        if i1<0:
            i1=0
        if i2>len(self.DIO)+1:
            i2=len(self.DIO)+1          
        for i in range(i1,i2):
            self.DIO_N=i
            rez=self.Read()
            print 'DIO-',rez[0],'-',rez[2],'-',rez[3],'|',
            print
        self.DIO_N=i0

    def All_lst(self):
        I0=self.DIO_N
        for i in range(len(self.DIO)):
            self.DIO_N=i
            rez=self.Read()
            print 'DIO-',rez[0],'-',rez[2],'-',rez[3],'|',
            print
        self.DIO_N=i0


    def All_0(self):
        N0=self.DIO_N
        for i in range(len(self.DIO)):
            sleep(0.1)
            self.DIO_N=i
            self.OFF()
        self.DIO_N=N0
        return 'All DIO 0'
#******************************************************************************
#******************************************************************************
#******************************************************************************
class PWM(object):
    
    def __init__(self,CORE,N):
        print 'PWM ',CORE.bus_n,hex(CORE.adr)
        self.bus=CORE.bus
        self.adr=CORE.adr
        self.N=N
        self.Dty=0
#******************************************************************************
    def Set(self,dty):
        self.Dty=dty
        try:
            if self.N in range(1,6):
                self.bus.write_byte_data(self.adr,self.N+3,self.Dty)
        except:
            print('Error , PWM  1 to 5, duty 0 to 255')
        return self.N,self.Dty
        
    def All_0(self):
        N0=self.N
        for i in range (1,6):
            sleep(0.1)
            self.N=i
            self.Set(0)
        self.N=N0
        return ' All PWM  set to 0 %'
        
    def OFF(self):
        self.Set(0)
#******************************************************************************
#******************************************************************************
#******************************************************************************
class Motor(object):
    
    def __init__(self,CORE,PWM,D_IO):
        print 'Motor ',CORE.bus_n,hex(CORE.adr)
        self.bus=CORE.bus
        self.adr=CORE.adr
        self.PWM=PWM
        self.DIO=D_IO
        self.dty=0
#******************************************************************************
    def CCW(self,dt):
        self.dty=dt
        self.PWM.Set(dt)
        sleep(0.01)
        self.DIO.Set(1) #0 output
        
    def CW(self,dt):
        self.dty=dt
        self.PWM.Set(255-dt)
        sleep(0.01)
        self.DIO.Set(2) #0 output        

    def BRK(self):
        self.DIO.Set(2)
        sleep(0.01)
        self.PWM.Set(255)
        sleep(0.01)

    def FRE(self):
        self.DIO.Set(2)
        sleep(0.01)
        self.PWM.Set(255)
        sleep(0.01)

    
    def SNS(self,dt):
        if dt<-255:
            dt=-255
            
        if dt>255:
            dt=255
            
        if dt<0:
            self.CW(abs(dt))
        if dt>0:
            self.CCW(dt)
        if dt==0:
            if self.Get_S()=="CCW":
                self.FRE()
            if self.Get_S()=="CW":
                self.BRK()
            
    
    def Get_S(self):
        if self.DIO.Read()[3]==0:
            return 1,'CCW'
        if self.DIO.Read()[3]==1:
            return -1,'CW'
    
    def Get_DTY(self):
        result=int(self.dty*self.Get_S()[0])
        return result
        
    def Ramp(self,N0,NF,STP,T):
        dtty=[]
        tt=[]
        dt=1.00*T/STP
        c0=clock()
        for i in range(STP+1):
            S=int(N0+i*(NF-N0)/STP)
            print S,dt*i,self.Get_DTY(),clock()-c0
            self.SNS(S)
            sleep(dt)
            dtty.append(self.Get_DTY())
            tt.append(i*dt)
        grid(20)
        self.SNS(NF)
        plot(tt,dtty)
            
    def Rvs(self,t):
        print 'Reversing'
        NN=-self.Get_DTY()
        self.Ramp(self.Get_DTY(),-self.Get_DTY(),50,t)
        self.SNS(NN)


#******************************************************************************
class Stepper(object):
        def __init__(self,CORE,S360):
            print 'PIC BiPol Stepper ',CORE.bus_n,hex(CORE.adr)
            self.bus=CORE.bus
            self.adr=CORE.adr
            self.s360=S360
            self.ang=360.0/self.s360

        def CCW(self,NP):
            self.np=NP
            self.bus.write_byte_data(self.adr,35,self.np)
            sleep(0.2+NP*0.045) # this is an empirical function

        def CW(self,NP):
            self.np=NP
            self.bus.write_byte_data(self.adr,36,self.np)
            sleep(0.2+NP*0.045)
            
        def A_CW(self,angle):
            NP=byte(angle/self.ang)
            self.CW(NP)

        def A_CCW(self,angle):
            NP=byte(angle/self.ang)
            self.CCW(NP)
        
        def FLW(self,ADC):
            self.AN=ADC
            W0=self.AN.Read()[1]
            sleep(0.2)
            W1=self.AN.Read()[1]
            #print W0,W1
            if abs(W1-W0)>3:
                if W1>=W0:
                    self.CCW((W1-W0)/4)
                if W1<W0:
                    self.CW((W0-W1)/4)
#******************************************************************************
class PWM_Stepper(object):
        def __init__(self,CORE,W1,W2,W3,W4):
            self.bus=CORE.bus
            self.adr=CORE.adr
            self.w1=W1
            self.w2=W2
            self.w3=W3
            self.w4=W4
            self.w1.All_0()
            print 'PIC BiPol PWM Stepper ',CORE.bus_n,hex(CORE.adr)
            
        def CW(self,DL,LVL,NP):
            self.dl=DL
            self.lvl=LVL
            self.N=NP
            for i in range(self.N):
#0
                self.w1.Set(self.lvl)
                sleep(self.dl)                
                self.w2.Set(0)
                sleep(self.dl)                
                self.w3.Set(self.lvl)
                sleep(self.dl)                
                self.w4.Set(0)
                sleep(self.dl)
#1
                self.w1.Set(0)
                sleep(self.dl)
                self.w2.Set(self.lvl)
                sleep(self.dl)
                self.w3.Set(self.lvl)
                sleep(self.dl)
                self.w4.Set(0)
                sleep(self.dl)
#2
                self.w1.Set(0)
                sleep(self.dl)
                self.w2.Set(self.lvl)
                sleep(self.dl)
                self.w3.Set(0)
                sleep(self.dl)
                self.w4.Set(self.lvl)
                sleep(self.dl)                
#3
                self.w1.Set(self.lvl)
                sleep(self.dl)
                self.w2.Set(0)
                sleep(self.dl)
                self.w3.Set(0)
                sleep(self.dl)
                self.w4.Set(self.lvl)
                sleep(self.dl)

            self.w1.All_0()
        
        def CCW(self,DL,LVL,NP):
            self.dl=DL
            self.lvl=LVL
            self.N=NP
            self.w1.Set(self.lvl)
            self.w2.Set(self.lvl)
            self.w3.Set(self.lvl)
            self.w4.Set(self.lvl)
           
            for i in range(self.N):
#3
                self.w1.Set(self.lvl)
                sleep(self.dl)
                self.w2.Set(0)
                sleep(self.dl)
                self.w3.Set(0)
                sleep(self.dl)
                self.w4.Set(self.lvl)
                sleep(self.dl)
#2
                self.w1.Set(0)
                sleep(self.dl)
                self.w2.Set(self.lvl)
                sleep(self.dl)
                self.w3.Set(0)
                sleep(self.dl)
                self.w4.Set(self.lvl)
                sleep(self.dl)                

#1
                self.w1.Set(0)
                sleep(self.dl)
                self.w2.Set(self.lvl)
                sleep(self.dl)
                self.w3.Set(self.lvl)
                sleep(self.dl)
                self.w4.Set(0)
                sleep(self.dl)

#0
                self.w1.Set(self.lvl)
                #sleep(self.dl)                
                self.w2.Set(0)
                #sleep(self.dl)                
                self.w3.Set(self.lvl)
                #sleep(self.dl)                
                self.w4.Set(0)
                sleep(self.dl)
            self.w1.All_0()

#******************************************************************************
#                 Autoconfig Global Variables        
#******************************************************************************

#==============================================================================    
def Auto_Core():
    C=None
    AN  =[]   # Analogues
    D   =[]   # Digitals
    PW  =[]   # PWMS 
    M   =[]   # Motors
    for i in range (1,7):
        PW.append(None)
        M.append(None)
    #init   anself config Core 
    C=CORE()
    C.test()
      
    for i in range(12):
        AN.append(ADC(C,i))
        
    for i in range(0,12):
        D.append(D_IO(C,i))
    D[0].All_0()
        
    for i in range(1,6):
        PW[i]=PWM(C,i)

    M[1]=Motor(C,PW[1],D[9])
    M[2]=Motor(C,PW[3],D[8])
    M[3]=Motor(C,PW[4],D[7])
    M[4]=Motor(C,PW[2],D[3])
    M[5]=Motor(C,PW[5],D[10])

    E=EEPROM(C)

    STPR=Stepper(C,128) # 128 is the steps/360 deg
    
    PW_STPR=PWM_Stepper(C,PW[1],PW[2],PW[3],PW[4])
    
    return C,AN,D,PW,M,E,STPR,PW_STPR

#******************************************************************************
#End of this version   T_Core_Class_9 extended DIO by CR March 2017
#******************************************************************************
C,AN,D,PW,M,E,STPR,PW_STPR=Auto_Core()[0:8] # go thru init

# Examples of use :

# Analogues are AN[0].Read() for example
 The module includes all needed functions for handling , reading and setting the PIC inputs and outputs.


No comments:

Post a Comment