色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

【試用報告】RP2040上的MicroPython環境中多線程編程

電子發燒友論壇 ? 來源:未知 ? 2023-04-18 09:15 ? 次閱讀

雙核介紹

BPI-Pico-RP2040官方介紹如下:



其核心是RP2040,采用的是ARM Cortex M0+ CPU內核,運行頻率高達 133 MHz。


比一般使用Cortex M0+的MCU更強大的是,RP2040使用了雙核ARM Cortex M0+,既然是雙核的,那么我們就可以在BPI-Pico-RP2040運行多線程程序了,更好的挖掘出其潛力來。


多線程了解

關于什么是多線程,本文不講,大家可以自行查找資料詳細了解。


為了更方便的進行測試,本次所有的實例,都是在python環境中進行的。


經過了解,circuitpython還不支持多線程,而micropython則已經提供支持。


不過micropython中的多線程還是實驗性質的支持,這從官方文檔中可以了解:MicroPython libraries ? _thread – multithreading support



micropython官方為RP2040提供的最新固件為v1.19.1,其已提供對多線程的支持。


因為micropython的多線程基于CPython中的_thread模塊,所以可以從Python官方文檔了解其具體用法:_thread --- 底層多線程 API


如果是開始使用多線程,那么先關注如下的調用,等熟悉了以后,再深入學習其他的:

  • _thread.start_new_thread(function, args[, kwargs]):開啟一個新線程

  • _thread.allocate_lock():返回一個新的鎖對象

  • lock.acquire(blocking=True, timeout=- 1):申請獲得鎖

  • lock.release():釋放鎖


本文中所有的實例代碼,都可以從以下地址獲取:

Pico(RP2040)上的MicroPython環境中多線程編程https://gitee.com/honestqiao/multithread_in_micropython_on_pico


基礎多線程

首先,用一個簡單的micropython程序,來控制板載的LED不同時間點亮和熄滅

# file: multicore_test01.py
import machine
import _thread
import utime


led = machine.Pin(25, machine.Pin.OUT)
led.off()


key = 0
start_time = 0
def run_on_core1():
global start_time
while start_time == 0:
pass


while True:
utime.sleep_ms(300)
print((utime.ticks_us()-start_time)//100000, "led on")
led.on()
utime.sleep_ms(700)

def run_on_core0():
global start_time
start_time = utime.ticks_us()
while True:
utime.sleep_ms(700)
print((utime.ticks_us()-start_time)//100000, "led off")
led.off()
utime.sleep_ms(300)


_thread.start_new_thread(run_on_core1, ( ))
run_on_core0()

(左右移動查看全部內容)


在RP2040的micropython環境中,程序默認在core0上運行,使用_thread.start_new_thread()啟動新的線程后,將會在core1上運行。


上面的程序運行后,具體輸出結果如下:



在run_on_core1中,先延時300ms,然后點亮led,再延時700ms,然后繼續循環


在run_on_core0中,先延時700ms,然后熄滅led,再延時300ms,然后繼續循環


從以上的輸出可以看到,點亮和熄滅led,都對應到了對應的時間點。


也許有人會說,這有啥用,我不用多線程,也完全可以在對應的時間點點亮和熄滅LED,用多線程豈不是多此一舉。


上面的例子,是一個基礎的多線程演示,其只是在兩個線程中,控制同一個LED,所以會覺得意義不大。如果我們的程序要同時做兩件不同的事情,那么每件事情在一個core上運行,互不干擾,就很重要的,在后面會有這樣的實例展示。


確認雙線程

在不同的開發板上,對多線程的支持,也是有差異的。


RP2040上的micropython,只能跑兩個線程,每個線程占用1個core,多了就會出錯。


我們可以用下面的程序進行驗證:

# file: multicore_test02.py


import machine
import _thread
import utime


def thread_1():
while True:
print("thread_1")
utime.sleep_ms(1000)


def thread_2():
while True:
print("thread_2")
utime.sleep_ms(1000)


_thread.start_new_thread(thread_1, ( ))
_thread.start_new_thread(thread_2, ( ))


while True:
print("main")
utime.sleep_ms(1000)

(左右移動查看全部內容)


運行上面的程序后,將會出現如下的錯誤信息



其原因在于,主程序本身,使用了core0,而使用_thread.start_new_thread()創建一個線程時,會自動的使用core1,第二次調用_thread.start_new_thread()再次創建一個線程時,無法再使用core1,所以就會出錯。


在core1上運行的子線程,需要使用_thread.start_new_thread()創建,所以其運行的需要使用一個函數進行調用作為入口。


而程序的主線程,運行在core0上,可以直接在程序主流程中寫運行邏輯,也可以寫一個函數調用,效果是一樣的。


后續的實例中,我們將使用run_on_core0()和run_on_core1()來區分在core0、core1的所運行的線程。



線程間交互


全局變量

通常時候,讓兩個線程,分別做各自獨立的事情,可以運行的很好。


但有的時候,我們可能還需要兩個之間,能夠有一些交流。


最簡單的方法,就是使用一個全局變量,然后兩個線程之間,都調用這個全局變量即可。


下面用一個簡單的程序進行演示:

# file: multicore_test03.py
import machine
import _thread
import utime


led = machine.Pin(25, machine.Pin.OUT)
led.off()


status = 0
def run_on_core1():
global status
while True:
if status:
led.on()
else:
led.off()
utime.sleep_ms(100)

def run_on_core0():
global status
while True:
status = 1 if not status else 0
utime.sleep_ms(1000)


_thread.start_new_thread(run_on_core1, ( ))
run_on_core0()

(左右移動查看全部內容)


在上面的程序中,core0上的程序,每隔1秒,將status取反一次。core1上的程序,則根據status的值,來點亮或者熄滅LED。


線程鎖

上面這個程序比較簡單,處理起來的速度很快,所以這么實用,不會有什么問題。


如果我們有一個程序需要兩個線程進行配合,例如主線程進行數據采集分析,而子線程進行數據的呈現,就有可能會出現問題了。


我們看一看下面的程序:

# file: multicore_test04.py
import machine
import _thread
import utime


led = machine.Pin(25, machine.Pin.OUT)
led.off()


status = 0
data = []
def run_on_core1():
global status, data
while True:
if status:
led.on()
else:
led.off()
str_data = ''.join(data)
print("str_data: len=%d content=%s" % (len(str_data), str_data))
utime.sleep_ms(1000)

def run_on_core0():
global status, data
while True:
status = 1 if not status else 0
data = []
for i in range(100):
data.append(str(status))
utime.sleep_ms(10)
utime.sleep_ms(1000)


_thread.start_new_thread(run_on_core1, ( ))
run_on_core0()

(左右移動查看全部內容)


在core0的主線程中,根據status的值,將data設置為100個0或者1;而在core1的子線程中,則將其值合并為字符串輸出出來,輸出的同時,顯示字符串的長度。


運行上面的程序后,實際輸出結果如下:



按說,其長度,要么是空,要么是100,可是實際結果卻會出現不為100的情況呢?


這是因為,core0上的主線程在操作data,core1的子線程也在操作data,兩者都是在同時進行的,而多個控制線程之間是共享全局數據空間,那么就會出現,core0上的主線程處理數據處理到到一半了,core1的子線程已經開始操作了,這樣就會出現問題,數據不完整了。


顯然,這種情況,是我們所不期望的。那要解決這種情況,可以用一個全局變量作為標志,主線程告訴子線程是否處理完成了,一旦處理完成了,子線程就可以開始處理了。


但線程調用庫本身,有更好的辦法,那就是鎖。


我們先看下面的程序:

# file: multicore_test05.py
import machine
import _thread
import utime


led = machine.Pin(25, machine.Pin.OUT)
led.off()


status = 0
data = []
def run_on_core1():
global status, data
while True:
if status:
led.on()
else:
led.off()
lock.acquire()
str_data = ''.join(data)
print("str_data: len=%d content=%s" % (len(str_data), str_data))
lock.release()
utime.sleep_ms(1000)

def run_on_core0():
global status, data
while True:
status = 1 if not status else 0
lock.acquire()
data = []
for i in range(100):
data.append(str(status))
utime.sleep_ms(10)
lock.release()
utime.sleep_ms(1000)


lock = _thread.allocate_lock()
_thread.start_new_thread(run_on_core1, ( ))
run_on_core0()

(左右移動查看全部內容)


在上面的程序中,啟動線程之前,使用 _thread.allocate_lock() 來獲取一個新的鎖,然后在core0的主線程中,處理數據前,使用 lock.acquire() 獲得鎖,處理完成后,再使用lock.release()釋放鎖。


一但一個線程獲得鎖,那么其他線程想要獲得該鎖時,只能等待直到這個鎖被釋放,也就是不能同時獲得,這在python中叫做互斥鎖。


因而,在core1的子線程,要輸出數據的時候,也使用同樣的機制來獲得和釋放鎖。


最終,data改變時,其他地方需要等待改變完成。data輸出時,其他地方也需要等待輸出完成。從而確保了任何時刻,對只有一個地方操作改數據。


運行上面的程序,就能得到理想的輸出了:



運行中啟動線程

前面演示的程序,都是在主線程中,啟動了子線程,然后并行運行。


在實際使用中,還可以在主線程中,按需啟動子線程。

我們先看下面的程序:

# file: multicore_test06.py
import machine
import _thread
import utime


def run_on_core1():
print("[core1] run thread")
utime.sleep_ms(100)


def run_on_core0():
while True:
print("[core0] start thread:")
_thread.start_new_thread(run_on_core1, ( ))
utime.sleep_ms(1000)

run_on_core0()

(左右移動查看全部內容)


在上面的程序中,core0上運行的主線程,會每過1秒啟動一個子線程。子線程在core1上運行完以后,會自動退出。


運行后,輸出如下:



需要特別注意的是,如果子線程還沒有退出,那么再次啟動,將會出現錯誤。


例如我們修改上面的程序的延時如下:

# file: multicore_test07.py
import machine
import _thread
import utime


def run_on_core1():
print("[core1] run thread")
utime.sleep_ms(1000)


def run_on_core0():
while True:
print("[core0] start thread:")
_thread.start_new_thread(run_on_core1, ( ))
utime.sleep_ms(100)

run_on_core0()

(左右移動查看全部內容)


運行后,就會出錯:

[core0] start thread:
[core1] run thread
[core0] start thread:
Traceback (most recent call last):
File "", line 17, in
File "", line 14, in run_on_core0
OSError: core1 in use


其原因就在于,子線程還沒有結束,主線程又再次啟動主線程了。


這在多線程編程中,是需要特別注意的問題。


要解決這個問題,可以使用前面主線程和子線程交互中的方法,例如使用一個全局變量表示子線程是否運行完成,或者使用鎖。


下面是一個使用鎖的程序:

# file: multicore_test08.py
import machine
import _thread
import utime


def run_on_core1():
lock.acquire()
print("[core1] run thread")
utime.sleep_ms(1000)
lock.release()


def run_on_core0():
while True:
print("[core0] start thread:")
lock.acquire()
_thread.start_new_thread(run_on_core1, ( ))
lock.release()
utime.sleep_ms(100)


lock = _thread.allocate_lock()
run_on_core0()

(左右移動查看全部內容)


運行后,輸出如下:

[core0] start thread:
[core1] run thread
[core0] start thread:
[core1] run thread
[core0] start thread:
[core1] run thread
[core0] start thread:
[core1] run thread



多線程的實例


雙線程做pwm和ws2812b

下面,再用一段稍微復雜一點點的程序,演示多線程的使用。

# file: multicore_test09.py
import machine
import _thread
import utime
from ws2812 import WS2812


led = machine.Pin(25, machine.Pin.OUT)
led.off()


BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
WHITE = (255, 255, 255)
COLORS = (BLACK, RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE)


ws = WS2812(3, 1) #WS2812(pin_num,led_count)
ws.pixels_fill(BLACK)
ws.pixels_show()


def run_on_core1():
while True:
for color in COLORS:
ws.pixels_fill(color)
ws.pixels_show()
utime.sleep_ms(200)

def run_on_core0():
duty = 0
step = 1
count = 0
while True:
led.on()
utime.sleep_ms(duty)
led.off()
utime.sleep_ms(10-duty)

count = count + 1
if count>10:
count = 0
duty = duty + step
if duty >= 10:
step = -1
if duty <= 0 :
step = 1


_thread.start_new_thread(run_on_core1, ( ))
run_on_core0()

(左右移動查看全部內容)


在上面的這段程序中,我們會在core0上運行的主線程中,控制GPIO25的輸出占空比,從而讓板載LED產生類似呼吸燈的效果。同時,還會在core1上運行的子線程中,控制板載WS2812B燈珠變色。


雙線程播放Bad Apple

最后,我們再用經典的Bad Apple,作為這篇文章的結尾。

# file: multicore_test10.py
from machine import SPI,Pin
from ssd1306 import SSD1306_SPI
import framebuf
import _thread
import utime


spi = SPI(1, 100000, mosi=Pin(11), sck=Pin(10))
display = SSD1306_SPI(128, 64, spi, Pin(9),Pin(8), Pin(1))


def run_on_core1():
global fbuf
while True:
if not fbuf == None:
display.fill(0)

lock.acquire()
display.blit(fbuf,19,0)
fbuf = None
lock.release()

display.show()


utime.sleep_ms(100)


def run_on_core0():
global fbuf
while True:
for i in range(1,139):
dirt = 'BAD_APPLE/' + str(i) + '.pbm'
print(i, dirt)
with open(dirt,'rb') as f :
f.readline()
f.readline()
data = bytearray(f.read())

lock.acquire()
fbuf = framebuf.FrameBuffer(data,88,64,framebuf.MONO_HLSB)
lock.release()

utime.sleep_ms(100)


fbuf = None
lock = _thread.allocate_lock()
_thread.start_new_thread(run_on_core1, ( ))
run_on_core0()

(左右移動查看全部內容)


上面的代碼,使用core0上運行的主線程,來從pbm文件中讀取需要呈現的圖片數據,而在core1上運行的子線程中,則使用讀取到的數據輸出到OLED進行顯示。


因為受限于Pico內置存儲的限制,并沒有存儲完整的Bad Apple數據,所以只播放了部分。如果感興趣,可以將數據放置到SD卡上,主線程讀取數據,子線程顯示數據,一樣絲滑流暢。



后記


多線程是個讓人有愛又恨的東西,用好了能有大作用,但是用不好可能會出現莫名其妙的問題,需要好好鉆研。本文只是一些較為基礎的研究,還比較淺顯,對于gc等方面,都尚未涉及,感興趣的讀者可以進一步深入了解。


在鉆研的過程中,參考了不少資料,對所有資料的貢獻者表示感謝。以下為參考到的部分資料列表:

  • 樹莓派Pico迷你開發板MicroPython多線程編程實踐

  • Multithreaded on Raspberry Pi Pico (MicroPython)

  • Raspberry Pi Pico Dual Core Programming

  • Multi Thread Coding on the Raspberry Pi Pico in Micropython

  • pico-micropython-examples

  • Raspberrypi Pico MicroPython Cookbook

  • MicroPython類庫 ? _thread --- 線程

  • ESP32上驅動OLED屏幕播放你想要的視頻



聲明本文由電子發燒友社區發布,轉載請注明以上來源。如需社區合作及入群交流,請添加微信EEFans0806,或者發郵箱liuyong@huaqiu.com。


更多熱點文章閱讀

  • 龍芯架構首款面向嵌入式應用的開發板,2K500開發應用實例

  • 基于32位RISC-V設計的互聯型微控制器,沁恒微CH32V307開發樣例

  • RK3568!四核64位ARMv8.2A架構,匯聚編譯源碼及實戰樣例

  • 尺寸僅有21mm*51mm,板邊采用郵票孔設計,合宙 Air105 核心板開發總結

  • 基于32位RISC-V高集成SoC,ADP-Corvette-T1開發板樣例及源碼!


原文標題:【試用報告】RP2040上的MicroPython環境中多線程編程

文章出處:【微信公眾號:電子發燒友論壇】歡迎添加關注!文章轉載請注明出處。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴

原文標題:【試用報告】RP2040上的MicroPython環境中多線程編程

文章出處:【微信號:gh_9b9470648b3c,微信公眾號:電子發燒友論壇】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    【社區工程師專題系列第九期】一個狂熱的開源愛好者和傳播者——喬楚

    。 3、BPI-Pico-RP2040 開發板體驗 RP2040編程 IO (PIO) 使用初探 RP2040
    發表于 11-19 18:18

    socket 多線程編程實現方法

    在現代網絡編程多線程技術被廣泛應用于提高服務器的并發處理能力。Socket編程是網絡通信的基礎,而將多線程技術應用于Socket
    的頭像 發表于 11-12 14:16 ?339次閱讀

    Python多線程和多進程的區別

    Python作為一種高級編程語言,提供了多種并發編程的方式,其中多線程與多進程是最常見的兩種方式之一。在本文中,我們將探討Python多線程
    的頭像 發表于 10-23 11:48 ?393次閱讀
    Python<b class='flag-5'>中</b><b class='flag-5'>多線程</b>和多進程的區別

    BQ24040應用報告

    電子發燒友網站提供《BQ24040應用報告.pdf》資料免費下載
    發表于 10-14 09:28 ?0次下載
    BQ24040應<b class='flag-5'>用報告</b>

    我用了3分鐘,從零實現了單片機的點燈開發!

    近日,小熊派悄悄的上線了一款新的Pico板,基于樹莓派RP2040芯片的:BearPi-Pico RP2040 樹莓派? 對,你沒看錯!樹莓派不僅是一家優秀的開源硬件品牌,更是一家優秀的芯片公司
    發表于 09-27 15:51

    龍芯2K0300蜂鳥板試用報告

    龍芯2K0300蜂鳥板試用報告 一、試用環境 操作系統和框架:Linux+QT5.15 交叉編譯工具鏈
    發表于 09-13 18:00

    PLC系統的背板電源保護應用報告

    電子發燒友網站提供《PLC系統的背板電源保護應用報告.pdf》資料免費下載
    發表于 09-13 09:10 ?0次下載
    PLC系統<b class='flag-5'>中</b>的背板電源保護應<b class='flag-5'>用報告</b>

    ubuntu上交叉編譯rp2040

    連接到rp2040 ,所以使用另一塊pico作為調試器,需要給pico調試器下載固件,也就是picoprobe 地址 https://github.com/Wiz-IO
    發表于 08-27 08:00

    樹莓派Pico 2發布,搭載RP2350雙核RISC-V和Arm Cortex-M33微控制器!

    RP2040 一樣輕松超頻到 250MHz 官方聲稱 RP2350 與 RP2040 在軟件和硬件都是向前兼容的(RP2350 兼容
    發表于 08-13 10:07

    pico-ice:RP2040 plus Lattice iCE40UP5K FPGA 開發板 介紹

    RP2040 提供的 FPGA 時鐘,可在 SW 控制下輕松對 FPGA 時鐘進行編程 RP2040 可以對 FPGA 進行編程,還提供直通 UART 功能 通過 SPI 與 FPG
    發表于 06-28 15:45

    多線程設計模式到對 CompletableFuture 的應用

    最近在開發 延保服務 頻道頁時,為了提高查詢效率,使用到了多線程技術。為了對多線程方案設計有更加充分的了解,在業余時間讀完了《圖解 Java 多線程設計模式》這本書,覺得收獲良多。本篇文章將介紹其中
    的頭像 發表于 06-26 14:18 ?343次閱讀
    從<b class='flag-5'>多線程</b>設計模式到對 CompletableFuture 的應用

    java實現多線程的幾種方式

    Java實現多線程的幾種方式 多線程是指程序包含了兩個或以上的線程,每個線程都可以并行執行不同的任務或操作。Java
    的頭像 發表于 03-14 16:55 ?689次閱讀

    python5種線程鎖盤點

    線程安全是多線程或多進程編程的一個概念,在擁有共享數據的多條線程并行執行的程序
    發表于 03-07 11:08 ?1586次閱讀
    python<b class='flag-5'>中</b>5種<b class='flag-5'>線程</b>鎖盤點

    基于樹莓派RP2040的解魔方機器人,7秒還原三階魔方

    地望著你,是時候亮出工程師的魔法神器了。今天特別分享@愛跑步的小何大佬的開源佳作——三階魔方還原機器人。三階魔方還原機器人-開源分享-這是一款基于樹莓派RP2040單片機設
    的頭像 發表于 01-13 08:04 ?1838次閱讀
    基于樹莓派<b class='flag-5'>RP2040</b>的解魔方機器人,7秒還原三階魔方

    基于樹莓派RP2040單片機設計的三階魔方還原機器人

    這是一款基于樹莓派RP2040單片機設計的三階魔方還原機器人,控制和魔方求解都使用單片機完成。對于隨機打亂的三階魔方,平均還原步驟數在21步左右。
    的頭像 發表于 01-12 13:37 ?1405次閱讀
    基于樹莓派<b class='flag-5'>RP2040</b>單片機設計的三階魔方還原機器人
    主站蜘蛛池模板: 成人a毛片久久免费播放| 欧美高清一区二区三| 国产精品亚洲欧美| Y8848高清私人影院软件优势| 中文字幕欧美一区| 中文无码熟妇人妻AV在线| 中文字幕无码A片久久| 亚洲中文字幕永久在线 | 青青久久久| 欧美亚洲韩日午夜| 青娱乐在线一区| 少妇内射视频播放舔大片| 色偷偷亚洲男人天堂| 午夜亚洲精品不卡在线| 无遮18禁在线永久免费观看挡| 视频一区精品自拍亚洲| 午夜爱情动作片P| 一个人在线观看免费高清视频在线观看| 亚洲精品美女久久777777| 伊人影院亚洲| chinesevideos原创麻豆| 国产成人精品免费视频大全办公室 | 青柠在线观看免费高清电视剧荣耀 | 国产精品97久久AV色婷婷| 国产深夜福利视频在线| 久久亚洲精品永久网站 | 韩国甜性涩爱| 欧美老少欢杂交另类| 性夜影院午夜看片| 2020国产成人精品视频人| 粗好大用力好深快点漫画| 欧美高清另类video| 色综合a在线| 中国videos1314tv| 国产精品美女久久久久浪潮AV| 久久视频这里只精品99热在线观看 | 老太婆性BBWBBW| 亚洲 小说 欧美 激情 另类| 中国成人在线视频| 国产亚洲精品AV麻豆狂野| 欧美亚洲另类图片|