Search code examples
pythonraspberry-pii2cpwm

software pwm using I2C with raspberry in python


i'm looking for a solution using a mcp23017 gpio-expander with raspberry pi as led-dimmer, but every 4-5sec there is a short flickering. i carried out that flickering is also there if i use gpio directly (comment/uncomment relevant parts in code if u try it) i cant use rpi.gpio software-pwm or pi-blaster because it is not usable via i2c, if you have a solution for getting this packages ready for i2c it would also be great i think the problem is somewhere in adressing GPIO, but i dont get it

--update-- it's not possible to get stable timings with software at raspberry pi

#!/usr/bin/python
# -*- coding: utf-8 -*-

# uncomment line 14-20 for using I2C and comment line 24-35, switch for using GPIO directly

import smbus
import time
import RPi.GPIO as GPIO

liste = [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# print liste #debugging ...
periodendauer = 0.001 # means 1000Hz

# # to send data to mcp23017
# b = smbus.SMBus(1) # 0 indicates /dev/i2c-0, muss auf 1 stehen (für rev2)
# while True:
    # for values in liste:
        # b.write_byte_data(0x20,0x14,values) #send data via smbus(I2C) to mcp23017
        # # print values #debugging only
        # time.sleep(periodendauer)


# to send data direct to gpio-pin
GPIO.cleanup()
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7, GPIO.OUT)
while True:
    for values in liste:
        if values > 0:
            values = True
        else:
            values = False
        GPIO.output(7,values)
        # print values #debugging only
        time.sleep(periodendauer)

Solution

  • Based on the comments I rewrited my answer for your question.

    I simplified your application with removing the I2C part and the comments and dropped the sleep function. I did that to show you how unreliable is the Raspbery Pi for this kind of precise timing in the way you want to use it. What I added to your code is a time measuring at the start and the end of the for loop, so it is now measuring a whole cycle processing of the "liste" array not the length of an individual "values".

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    import time
    import RPi.GPIO as GPIO
    import sys
    
    liste = [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(7, GPIO.OUT)
    while True:
        ms = time.time() * 1000.0
        for values in liste:
            if values > 0:
                values = True
            else:
                values = False
            GPIO.output(7,values)
        me = time.time() * 1000.0 - ms
        sys.stdout.write("\r{0:4.4f}".format(me)),
        sys.stdout.flush()
    

    I have a Banana Pi at home hat have the same GPIO outputs, but please run it on a Raspberry, you will have the same result (maybe with longer cycle times). For me the results were between 4-5ms with several 6ms length pulses and sometimes it went over 10ms. That's why you have flickering led.

    For an I2C based solution I would suggest a dedicated I2C PWM driver board to produce a smooth PWM signal such as the PCA9685 from NXP.