Raspberry - PI
Analogue / PWM / DIO/ DC motor / Stepper
I2C Expansion Board

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.Analogue / PWM / DIO/ DC motor / Stepper
I2C Expansion Board
also for:
other R-PI replacements
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.
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 :
- Generate an I2C slave device with programmable I2C address (1 to 127).
- Build an I2C extension for Analog and digital inputs (I2C Sensor Core).
- Build an I2C extension for Digital Outputs including PWM outputs.
- Expand the PWM and DIO to defined DC Motor objects
- Expand PWM outputs for Stepper motor command mono and bipolar
- Stream Serial Data with RS232 in text mode (for non Linux devices)
- 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
http://www.microchip.com/wwwproducts/en/PIC18F46K80
Five CCP/ECCP modules:
- Four Capture/Compare/PWM (CCP) modules
- One Enhanced Capture/Compare/PWM(ECCP) modul
- Timer0: 8/16-bit timer/counter with 8-bitprogrammable prescaler
- Timer1, Timer3: 16-bit timer/counter
- Timer2, Timer4: 8-bit timer/counter
Configurable Reference Clock Output
Charge Time Measurement Unit (CTMU):
- Capacitance measurement
- Time measurement with 1 ns typical resolution
- Integrated voltage reference
One Master Synchronous Serial Port
(MSSP) module:
- 3/4-wire SPI (supports all four SPI modes)
- I2C TM Master and Slave modes
- LIN/J2602 support
- Auto-Baud Detect (ABD)
- Auto-acquisition and Sleep operation
- Differential Input mode of operation
- 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.
- The Main file is called Core15.py
- The main program file defines usable objects which interact with the PIC hardware.
- The PIC18746K80 was programmed under MicroPascal using PICKit3 as programming interface thru MpLab .
- The source for PIC will be described in an future post.
- 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
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
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 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
******************************************************************************
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
- The I2C_CORE address can be set with the Pic_Addr_Change function. The new address will take effect after Reset.
- 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)
- 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.
- 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)
- Further development will simplify the interaction by objectifying the concept.
- Read Analog inputs (ADC_R(address,i):) 0 to 9 with 12 bit conversion (0-4095) 0-5 V range
- Reading 10 Digital I/O (DIO)(Read_DIO(address,i):) will return the stage of the DIO pin (0/1)
- 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.
- The 5 PWM's available , set at 3000 HZ can be set with (Set_PWM(adr,PWM_NR,duty)).
# -*- 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],'|',
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],'|',
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