1. I wrote a program (IDE=Thonny) to drive some WS2812 LEDs, copying the sample program on the MicroPython book. It works.
2. I modified it to create some different figures on the LED. OK
3. I added a communication routine to receive from a Raspberry PI some commands to choose which figure to activate. The corresponding figure is activated. It's OK and it runs for hours (but obviously, during this phase the communication can't go on)
4. Then, in order to manage the LEDs while the communication is going on, I moved this routine to a second thread whose only task is to receive commands ; it then sends the received data to the first thread by means of global area and a flag.
This newly arranged situation works FOR SOMETIME (6-7 new commands) but suddenly stops and there is no way to gain access to the program in Thonny (no ^C etc) environment. You can only power off/on or reset through the RUN pin. I thought there could be interference between comms and threading so I canceled the communication and wrote a simple thread which simulates periodically (3 sec) to have received data from the comms and sends them to the first thread.
Again, after a while, it stops.
To quickly detect the problem I suggest to use the instruction :
DataToRead[1] = ord('3')
which is the command to see a "LED moving around". Usually the Pico stops running after 5-6 "LED back and forth".
If you substitute
DataToRead[1] = ord('1')
which is the command to turn every LED on with a single colour, in this case the system looks to be running for some minutes but anyway it stops, again without giving the possibility to halt it to debug
Thanks everybody
Michele
Programs running (forever) without thread :
Code: Select all
import array, machine, utime
from machine import Pin, UART
import _thread
from rp2 import PIO, StateMachine, asm_pio
# Configure the number of WS2812 LEDs and GPIO driver
NUM_LEDS = 14
GPIO_NUM = 4
BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
GREEN = (0, 255, 0)
ORANGE = (255, 127, 0)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
WHITE = (255, 255, 255)
COLORS = (BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, WHITE)
STX=2
ETX=3
NAK=21
global DataToRead
DataToRead = bytearray('0000') # Area shared between main task and thread
global DataToReadReady
DataToReadReady= 0 # Flag telling there are data to read in shared area
@asm_pio(sideset_init=PIO.OUT_LOW, out_shiftdir=PIO.SHIFT_LEFT, autopull=True, pull_thresh=24)
def ws2812():
T1 = 2
T2 = 5
T3 = 3
wrap_target()
label("bitloop")
out(x, 1) .side(0) [T3 - 1]
jmp(not_x, "do_zero") .side(1) [T1 - 1]
jmp("bitloop") .side(1) [T2 - 1]
label("do_zero")
nop() .side(0) [T2 - 1]
wrap()
# Create the StateMachine with the ws2812 program, outputting on pin
sm = StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(GPIO_NUM))
# Start the StateMachine, it will wait for data on its FIFO.
sm.active(1)
# Display a pattern on the LEDs via an array of LED RGB values (ogni elemento = INT = 4 bytes)
ar = array.array("I", [0 for _ in range(NUM_LEDS)])
##########################################################################
brightness = 255
def pixels_show():
"""
Output "ar" array, after dimmering according to brightness
"""
dimmer_ar = array.array("I", [0 for _ in range(NUM_LEDS)])
for i,c in enumerate(ar):
r = int(((c >> 8) & 0xFF) * brightness)
g = int(((c >> 16) & 0xFF) * brightness)
b = int((c & 0xFF) * brightness)
dimmer_ar[i] = (g<<16) + (r<<8) + b
sm.put(dimmer_ar, 8)
utime.sleep_ms(10)
def set_a_pixel(i, color):
"""
set a pixel (LED) after the required color
"""
ar[i] = (color[1]<<16) + (color[0]<<8) + color[2]
def all_switched_on(color):
"""
every pixel with the same colour
"""
for i in range(len(ar)):
set_a_pixel(i, color)
pixels_show()
def roll_1_LED(color, wait):
# roll/shift a LED back and forth (choice of colour)
for ii in range(1, NUM_LEDS):
set_a_pixel(ii, color)
utime.sleep(wait)
set_a_pixel(ii-1, BLACK)
pixels_show()
for ii in range(NUM_LEDS-2, -1,-1):
set_a_pixel(ii, color)
utime.sleep(wait)
set_a_pixel(ii+1, BLACK)
pixels_show()
set_a_pixel(0,BLACK)
pixels_show()
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Second thread section (Here not used as a thread):
# This is a simulation of the original program which is communicating on a serial line to a master device.
# Receives information to turn on the WS2812 LEDs in different ways.
# The received data are tranferred to the first thread through a global area
# IN THE SIMULATION, every 4 seconds, it generates a DataToRead buffer to send a command tothe first thread
# the data are arranged in a 4 bytes array (ASCII) :
# byte 0 = notused
# byte 1 = command ( eg. '1'= switch LEDs on with same color, '3' = shift (roll) an LED )
# (other commands are not useful to understand the problem)
# byte 2 = color
# byte 3 = brightness
# After tranferring the command to the first thread, the second one sleeps for 3 second, before generating a new command,
# so that they work alternating.
# N.B. This simulation shows that the problem doesn't come from the serial communication.
# Maybe the problem comes from the co-existence of a second thread together with the State Machine
# If the program is run without the thread, i.e by calling the GestSeriale_thread() from the main program,
# the program runs witjout stopping. See Prova_NO_Thread
colorethread = 0
def GestSeriale_thread():
global DataToRead, DataToReadReady, colorethread
colorethread = (colorethread % 7) + 1 #cycle on 8 colors, from red to black
DataToRead[0] = ord('A')
DataToRead[1] = ord('3')
DataToRead[2] = colorethread + 0x30
DataToRead[3] = ord('2')
DataToReadReady +=1 # a new set of data ready in DataToRead
utime.sleep(1)
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Startup = switch off every LED
color= 0
brightness= 0
all_switched_on(COLORS[color])
pixels_show()
#_thread.start_new_thread(GestSeriale_thread, ())
# First thread / MAIN program = permanently waiting for a command from the second thread, who received it through the serial line 0
# the true original program
while (True):
GestSeriale_thread()
if (DataToReadReady >0):
DataToReadReady -=1
print(DataToRead)
color= DataToRead[2] & 0xF # quindi imposta colore e luminosita
brightness= (DataToRead[3] & 0xF)/10
if (DataToRead[1]==ord('1')): # e poi scegli il programma
all_switched_on(COLORS[color])
elif (DataToRead[1]==ord('2')):
arcobaleno()
elif (DataToRead[1]==ord('3')):
roll_1_LED(COLORS[color], 0.05)
elif (DataToRead[1]==ord('4')):
sfuma_arcobaleno()
elif (DataToRead[1]==ord('5')):
scorri_1_colore(COLORS[color], 0.05)
#utime.sleep_ms(200)
Code: Select all
import array, machine, utime
from machine import Pin, UART
import _thread
from rp2 import PIO, StateMachine, asm_pio
# Configure the number of WS2812 LEDs and GPIO driver
NUM_LEDS = 14
GPIO_NUM = 4
BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
GREEN = (0, 255, 0)
ORANGE = (255, 127, 0)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
WHITE = (255, 255, 255)
COLORS = (BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, WHITE)
STX=2
ETX=3
NAK=21
global DataToRead
DataToRead = bytearray('0000') # Area shared between main task and thread
global DataToReadReady
DataToReadReady= 0 # Flag telling there are data to read in shared area
@asm_pio(sideset_init=PIO.OUT_LOW, out_shiftdir=PIO.SHIFT_LEFT, autopull=True, pull_thresh=24)
def ws2812():
T1 = 2
T2 = 5
T3 = 3
wrap_target()
label("bitloop")
out(x, 1) .side(0) [T3 - 1]
jmp(not_x, "do_zero") .side(1) [T1 - 1]
jmp("bitloop") .side(1) [T2 - 1]
label("do_zero")
nop() .side(0) [T2 - 1]
wrap()
# Create the StateMachine with the ws2812 program, outputting on pin
sm = StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(GPIO_NUM))
# Start the StateMachine, it will wait for data on its FIFO.
sm.active(1)
# Display a pattern on the LEDs via an array of LED RGB values (ogni elemento = INT = 4 bytes)
ar = array.array("I", [0 for _ in range(NUM_LEDS)])
##########################################################################
brightness = 255
def pixels_show():
"""
Output "ar" array, after dimmering according to brightness
"""
dimmer_ar = array.array("I", [0 for _ in range(NUM_LEDS)])
for i,c in enumerate(ar):
r = int(((c >> 8) & 0xFF) * brightness)
g = int(((c >> 16) & 0xFF) * brightness)
b = int((c & 0xFF) * brightness)
dimmer_ar[i] = (g<<16) + (r<<8) + b
sm.put(dimmer_ar, 8)
utime.sleep_ms(10)
def set_a_pixel(i, color):
"""
set a pixel (LED) after the required color
"""
ar[i] = (color[1]<<16) + (color[0]<<8) + color[2]
def all_switched_on(color):
"""
every pixel with the same colour
"""
for i in range(len(ar)):
set_a_pixel(i, color)
pixels_show()
def roll_1_LED(color, wait):
# roll/shift a LED back and forth (choice of colour)
for ii in range(1, NUM_LEDS):
set_a_pixel(ii, color)
utime.sleep(wait)
set_a_pixel(ii-1, BLACK)
pixels_show()
for ii in range(NUM_LEDS-2, -1,-1):
set_a_pixel(ii, color)
utime.sleep(wait)
set_a_pixel(ii+1, BLACK)
pixels_show()
set_a_pixel(0,BLACK)
pixels_show()
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Second thread section :
# This is a simulation of the original program which is communicating on a serial line to a master device.
# Receives information to turn on the WS2812 LEDs in different ways.
# The received data are tranferred to the first thread through a global area
# IN THE SIMULATION, every 4 seconds, it generates a DataToRead buffer to send a command tothe first thread
# the data are arranged in a 4 bytes array (ASCII) :
# byte 0 = notused
# byte 1 = command ( eg. '1'= switch LEDs on with same color, '3' = shift (roll) an LED )
# (other commands are not useful to understand the problem)
# byte 2 = color
# byte 3 = brightness
# After tranferring the command to the first thread, the second one sleeps for 4 second, before generating a new command,
# so that they work alternating.
# N.B. This simulation shows that the problem doesn't come from the serial communication.
# Maybe the problem comes from the co-existence of a second thread together with the State Machine
# If the program is run without the thread, i.e by calling the GestSeriale_thread() from the main program,
# the program runs witjout stopping. See Prova_NO_Thread
colorethread = 0
def GestSeriale_thread():
global DataToRead, DataToReadReady, colorethread
while True:
colorethread = (colorethread % 7) + 1 #cycle on 8 colors, from red to black
DataToRead[0] = ord('A')
DataToRead[1] = ord('3')
DataToRead[2] = colorethread + 0x30
DataToRead[3] = ord('2')
DataToReadReady +=1 # a new set of data ready in DataToRead
utime.sleep(3)
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Startup = switch off every LED
color= 0
brightness= 0
all_switched_on(COLORS[color])
pixels_show()
_thread.start_new_thread(GestSeriale_thread, ())
# First thread / MAIN program = permanently waiting for a command from the second thread, who received it through the serial line 0
# the true original program
while (True):
if (DataToReadReady >0):
DataToReadReady -=1
print(DataToRead)
color= DataToRead[2] & 0xF # quindi imposta colore e luminosita
brightness= (DataToRead[3] & 0xF)/10
if (DataToRead[1]==ord('1')): # e poi scegli il programma
all_switched_on(COLORS[color])
elif (DataToRead[1]==ord('2')):
arcobaleno()
elif (DataToRead[1]==ord('3')):
roll_1_LED(COLORS[color], 0.05)
elif (DataToRead[1]==ord('4')):
sfuma_arcobaleno()
elif (DataToRead[1]==ord('5')):
scorri_1_colore(COLORS[color], 0.05)
utime.sleep_ms(200)