PCA9685 + LEDbar - sterowanie PWM w Pythonie

Moderator: bbiernat

Post Reply
User avatar
pancio
Administrator
Posts: 63
Joined: 18 September 2013, 23:02 - Wed
Location: SILESIA

PCA9685 + LEDbar - sterowanie PWM w Pythonie

Post by pancio »

Wstęp

Dziś chciałbym zaprezentować moduł służący do sterowania 16-ma serwomechanizmami z wykorzystaniem magistrali i2c. Układem wykonawczym jest PCA9685, który jest scalonym 16 kanałowym generatorem PWM o rozdzielczości 12 bitów. Prezentowany moduł posiada złącza typu goldpin do których można wpiąć bezpośrednio modelarskie wtyczki serwomechanizmów. Zaletą układu jest możliwość pracy zarówno w standardach 3v3 i 5V zależnie od zasilania ale co najważniejsze: serwomechanizmy zasilane są niezależnie napięciem do 6V. Z noty producenta możemy wyczytać również, że zakres regulacji PWM wynosi od 40 do 1000Hz. Prezentowany moduł możemy kupić za mniej niż 2$. My wykorzystamy nasz PCA9685 do sterowania LED-bara, czyli listwy diod LED. Oczywiście nic nie stoi na przeszkodzie by zasilać stopnie mocy i przykładowo wysterować całe taśmy LED :-)
Moduł zawierający układ PCM9685
Moduł zawierający układ PCM9685
Układ:
Sposób podłączenia naszego modułu jest następujący: goldpiny poszczególnych kanałów PWM podłączamy do katod diod LED. Anody zaś przez rezystory ograniczające (u mnie jest to 300 Ohm przy zasilaniu 3V3) do zasilania układu... jeśli potrzebny będzie schemat, proszę o informacje :-)

Program sterujący
Zmierzenie się z tematem nie jest trywialne ze względu na stopień skomplikowania instrukcji serwisowej (sic!), jednak udało ni się w wystarczającym stopniu rozszyfrować rejestry i ich znaczenie. Program zasadniczo wyjaśnia tok postępowania, konfiguracji i sterowania układem, jednak jeśli oczekujesz większej swobody - odsyłam do wnikliwej analizy serwisówki.

Code: Select all

#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#

from I2C import Device
import smbus
import os

import time

#Register defines from data sheet
PCA9685_MODE1 	=	0x00 # location for Mode1 register address
PCA9685_MODE2 	=	0x01 # location for Mode2 reigster address

PCA9685_LED0 	=	0x06 # location for start of LED0 register
PCA9685_LED0_OFF=	0x08 # location for start of LED0_OFF register

PCA9685_ALL_LED_ON	=	0xfa	# enable all LEDs
PCA9685_ALL_LED_OFF	=	0xfc	# disable all LEDs
PCA9685_PRE_SCALE	=	0xfe	# PWM frequency PRE_SCALE register

# Details regarding MODE1_REG
PCA_MODE1_ALLCALL	=	0b00000001
PCA_MODE1_SLEEP		=	0b00010000
PCA_MODE1_AI		=	0b00100000
PCA_MODE1_SOFTRESET	=	0b10000000

#The default I2C address
PCA9685_I2CADDR_DEFAULT 	= 0x40

# PCA9685 Address:
ADDRESSpca = PCA9685_I2CADDR_DEFAULT

# I2C BUS: 1 - for CT, 4 - for PC with Ch341A Interface 

BUS = 1
#BUS = 4

# initialize I2C BUS	
pca=Device(ADDRESSpca, BUS)

#functions:
def initPCA ():
	""" Initialize PCA9685. For PWM = 1000Hz PRE_SCALE = 5 = (25000000/(4096x1000)-1) """
	time.sleep(0.1)
	pca.write8(PCA9685_MODE1,(0x00 |PCA_MODE1_ALLCALL | PCA_MODE1_SLEEP)) 		# SLLEP mode is necessary in order to set PRE_SCALE Register 	
	pca.write8(PCA9685_PRE_SCALE,5) 											# PWM freq = 1000Hz
	pca.write8(PCA9685_MODE1,(0x00 |PCA_MODE1_ALLCALL | PCA_MODE1_SOFTRESET)) 	# comeback to NORMAL MODE with SOFTRESET possibility   
	pca.write8(PCA9685_MODE2,0b00010000)										# set output mode to inverted 
	state = pca.readU8(PCA9685_MODE1)
	return state

def readLEDonValue (LEDnumber):
	""" Read LEDon value """
	if (LEDnumber >= 0 and LEDnumber <16):
		LEDonLSB = pca.readU8(PCA9685_LED0 +4*LEDnumber)
		LEDonMSB = pca.readU8((PCA9685_LED0 +4*LEDnumber)+1)
		value = LEDonMSB*256 + LEDonLSB
		return value
	else:
		return "Wrong LED number - readLEDonValue()"


def readLEDoffValue (LEDnumber):
	""" Read LEDoff value """
	if (LEDnumber >= 0 and LEDnumber <16):
		LEDoffLSB = pca.readU8(PCA9685_LED0_OFF +4*LEDnumber)
		LEDoffMSB = pca.readU8((PCA9685_LED0_OFF +4*LEDnumber)+1)
		value = LEDoffMSB*256 + LEDoffLSB
		return value
	else:
		return "Wrong LED number - readLEDoffValue()"
				
def writeLED (LEDnumber, LEDon, LEDoff):
	""" LEDon and LEDoff are 12bit values (0-4095) """
	if (LEDnumber >= 0 and LEDnumber <16):
		if (LEDon < 0 and LEDon > 0x1000):
			LEDon = 0x1000
		if (LEDoff < 0 and LEDoff > 0x1000):
			LEDoff = 0x1000

		LEDonMSB = (LEDon & 0b1111111100000000) >> 8
		LEDonLSB = LEDon & 0b0000000011111111
		
		LEDoffMSB = (LEDoff & 0b1111111100000000) >> 8
		LEDoffLSB = LEDoff & 0b0000000011111111		
		
		pca.write8(PCA9685_LED0 + 4*LEDnumber,LEDonLSB)
		pca.write8((PCA9685_LED0 + 4*LEDnumber)+1,LEDonMSB)
		pca.write8((PCA9685_LED0 + 4*LEDnumber)+2,LEDoffLSB)
		pca.write8((PCA9685_LED0 + 4*LEDnumber)+3,LEDoffMSB)
		
	else: 
		return "Wrong LED number - writeLED()"
	
def setLEDon (LEDnumber):
	""" Set LED ON (Full Light) """
	
	pca.write8(PCA9685_LED0 + 4*LEDnumber,0x00)
	pca.write8((PCA9685_LED0 + 4*LEDnumber)+1,0x10)
	pca.write8((PCA9685_LED0 + 4*LEDnumber)+2,0x00)
	pca.write8((PCA9685_LED0 + 4*LEDnumber)+3,0x00)

	
def setLEDoff (LEDnumber):
	""" Set LED off """
	
	pca.write8(PCA9685_LED0 + 4*LEDnumber,0x00)
	pca.write8((PCA9685_LED0 + 4*LEDnumber)+1,0x00)
	pca.write8((PCA9685_LED0 + 4*LEDnumber)+2,0x00)
	pca.write8((PCA9685_LED0 + 4*LEDnumber)+3,0x10)
	

#MAIN:
state = initPCA()
#print "MODE1: ", hex(state)
#print "PRESCALLER:", hex(pca.readU8(PCA9685_PRE_SCALE))

time.sleep(3)

for i in range(0,10):
	setLEDoff(i)

for i in range(0,10):
	setLEDon(i)
	time.sleep(.1)

for i in range(0,10):
	setLEDoff(i)
	time.sleep(.1)

for i in range(0,10):
	setLEDon(i)
	time.sleep(.1)

time.sleep(2)

#for i in range(0,10):
#	setLEDoff(i)
#	time.sleep(.10)

	
writeLED(0,0x001,0x000)
writeLED(1,0x200,0x000)
writeLED(2,0x400,0x000)
writeLED(3,0x600,0x000)
writeLED(4,0x800,0x000)
writeLED(5,0xa00,0x000)
writeLED(6,0xc00,0x000)
writeLED(7,0xe00,0x000)
writeLED(8,0xf00,0x000)
writeLED(9,0xf80,0x000)

time.sleep(3)

writeLED(9,0x001,0x000)
writeLED(8,0x200,0x000)
writeLED(7,0x400,0x000)
writeLED(6,0x600,0x000)
writeLED(5,0x800,0x000)
writeLED(4,0xa00,0x000)
writeLED(3,0xc00,0x000)
writeLED(2,0xe00,0x000)
writeLED(1,0xf00,0x000)
writeLED(0,0xf80,0x000)

time.sleep(3)

for i in range(0,10):
	setLEDoff(i)
	time.sleep(.10)
	
A tak to wygląda na żywo :-) :

https://youtu.be/LS7mrRDXs5s
Attachments
PCA9685.zip
(2.88 KiB) Downloaded 1586 times
PCA9685.pdf
(399.2 KiB) Downloaded 1585 times

Post Reply