tag:blogger.com,1999:blog-100622042024-03-16T13:50:30.153+08:00阿呆的blogCodyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.comBlogger252125tag:blogger.com,1999:blog-10062204.post-57311388779269665932023-06-28T14:20:00.005+08:002023-06-28T20:22:40.333+08:00金融自動系統<p> 前言:</p><p>2014年時,曾以Java為基礎開發了股市自動交易系統 - JavaATS,後來不了了之。<br />最近以先前的架構為基礎重新開發,系統改以Python語言為基礎,以PostgreSQL和MQTT通訊協定為核心,重新設計成開發式架構。<br />因為系統無法達成自動交易的目標,因此命名為金融自動排程系統。<br />這篇會描述系統架構。</p><p>系統架構:</p><p>系統延續JavaATS的排程器核心,以Python重新實做。<br />因為科技的進步,原先Configure Manager使用的XML現在直接改用ini + PostgreSQL,原本定義在XML內的Task,都改定義到SQL中。<br />原先各元件之間,透過一個Socket Server交換資訊,同樣因為科技的進步,現在直接透過MQTT就能達成,Socket Server交換資訊時的資料格式問題,現在透過json能夠方便的進行資料交換。<br /></p><p>TimerServer:</p><p>這是整個系統的核心,它以Crontab語法為基礎,在SQL中定義多個Task,TimerServer會以while loop方式每15秒撈取一次SQL內的Task List,並比對當下時間和Task Crontab時間,符合就執行。<br />執行時,透過Python的os.system(),以及定義在SQL內的Task欄位,能夠指定直譯器以及程式參數,例如:<br />python3 Downloader.py<br />ts-node-script stock_invest_downloader.ts<br />python3 dir_scanner.py /tmp/data/</p><p>Crontab格式定義如下:<br /><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi3B4lQ1m1_d9fkh44E779xPV7UCl6yfa46ZWFABdXdv3qgsmim-XdPqrwSC97_SPKuAn-SGpBMngIcVOPxyBUdjutae-dCKkt3P0oR8UqaoHARanI98BBvbmYyIWi_aJhRZ17RT_weTadRvKqhODTz_1qtZRpo3fEuKYEwxQtym_AXuo5oee71GA" style="clear: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img alt="" data-original-height="428" data-original-width="712" src="https://blogger.googleusercontent.com/img/a/AVvXsEi3B4lQ1m1_d9fkh44E779xPV7UCl6yfa46ZWFABdXdv3qgsmim-XdPqrwSC97_SPKuAn-SGpBMngIcVOPxyBUdjutae-dCKkt3P0oR8UqaoHARanI98BBvbmYyIWi_aJhRZ17RT_weTadRvKqhODTz_1qtZRpo3fEuKYEwxQtym_AXuo5oee71GA=s16000" /></a></p><p>延續JavaATS的TimerServer,Crontab的語法基本上能做到特定日期時間執行、週期執行、每星期特定天執行。</p><p>多機跨網路系統:</p><p>TimerServer啟動時包括一個config.ini檔案,裡面定義了SQL的帳號、密碼、DB,一個稱為MY_RUNNER_NAME的名稱定義。<br />而在資料庫的Task List中,每個Task都包含一個runner的欄位,同樣的,TimerServer在檢查Task List時,會比對Task的runner和自己的MY_RUNNER_NAME是否一致,或者runner是否為空,如果為空或者一致,則進一步比對時間是否符合,符合則執行。<br />上述的runner設計,讓TimerServer變成了多機跨網路的系統,可在多台PC甚至樹莓派上執行,每台電腦都透過SQL撈取Task List,比對後判斷是否是相對應的機器和相對應的時間,符合就會執行,達到多機器跨網路運作。</p><p>圖形化管理元件:<br /></p><p>Python支援Qt,在Blog以前的文章中寫過多次Qt文章,這裡緬懷良葛格,以前開發Qt程式時,良葛格是少數中文的Qt教學,而且不同於書籍和官方教材,他的文章內容精準,看得出是學過寫過得人,才寫得出的教學文章,可惜他竟然走了。<br />透過PySide2,能夠方便的新增、修改、刪除Task List的Task。</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjSunUaRqq1QwGt8hpnz7jjd_MtPzfl0de_lURF9vatV7FPwPqfibKajzJRUuACMGCatf3FgzFcP7ydUf13XVpMR7t2b-jza5EAOJzWq7Qnd0nCNZ9Dpy4i6fKkmpJ0FfWuv9v7o7GxJd8mMTzO43TK_AW5-ocZSfwd0tbjrmVZWTt-FU-INOXImA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="522" data-original-width="563" height="522" src="https://blogger.googleusercontent.com/img/a/AVvXsEjSunUaRqq1QwGt8hpnz7jjd_MtPzfl0de_lURF9vatV7FPwPqfibKajzJRUuACMGCatf3FgzFcP7ydUf13XVpMR7t2b-jza5EAOJzWq7Qnd0nCNZ9Dpy4i6fKkmpJ0FfWuv9v7o7GxJd8mMTzO43TK_AW5-ocZSfwd0tbjrmVZWTt-FU-INOXImA=w575-h522" width="575" /></a></div><br /><p></p><p>模組化元件:</p><p>只有TimerServer定時執行顯然不夠,要能建構出金融自動系統,至少還需要歷史資料、證券公司API行情程式、證券公司API下單程式。</p><p>以歷史資料而言,現在能夠過Yahoo Finance API的Python Package,直接撈取歷史資料,後續透過Python PostreSQL Package就能將資料寫入SQL中。<br />加密貨幣?<br />加密貨幣交易所也是有提供API的,NodeJS?<br />前面提到,TimerServer可透過指定直譯器執行,不一定是Python,當然也可以是NodeJS,如前面範例提到的,ts-node-script就蠻方便的。</p><p>證券API串接:</p><p>我使用的證券提供c#的API,原則上是PC的下單應用程式將Component拆出來,透過c#呼叫的,因此,Windows是必要的。<br />目前我開了一個Windows VM,用Visual Studio 2015的c#開發2隻程式,一隻為即時價格註冊和擷取程式,一隻為下單程式,證券公司的API中,即時價格擷取是一個API,下單是一個API。<br />因為以前的經驗,為了縮短開發時間,方便未來證券公司API程式改版。<br />我是以證券公司提供的範例程式直接修改,將視窗Dialog隱藏,增加右下角System Notify,然後加上config.ini、MQTT的相關程式碼。<br />作用,<br />即時價格擷取:<br />能透過config.ini、SQL撈取要註冊取得即時價格的Symbol,當取得即時價格後,透過MQTT發送。</p><p>即時下單程式:<br />透過MQTT取得下單指令,進行下單,並透過MQTT、LineBot回報委託情況。</p><p>上述2隻程式開發後,連同API一起放在一個Windows VM,並排程開機、關機。</p><p><br /></p><p>資料分析與技術分析:</p><p>嗯~這是個大哉問,如果有答案就不會寫這篇了~啊~不是。<br />這個以TimerServer為基礎的系統,能夠方便的加上各種分析模塊,我們以典型的技術分析當作例子:<br />寫一隻Python程式,內容是<br />每日從SQL中撈取歷史資料,透過Python的TALib計算均線(Simple MA),然後根據是否Cross,從MQTT或SQL上發出或寫入觸發信號。<br />當然,也可以是透過觸發信號直接透過MQTT發出下單指令。</p><p>來點更複雜的吧。<br />OK......</p><p>結論:</p><p>透過新版的金融自動(排程)系統,能夠透過開發各個模塊疊加,形成一個多功能跨網路的金融系統,因為以模塊化設計,各種功能都能實做,小到下載歷史資料,大到結合AI測試都能開展,而且透過SQL和MQTT,能夠讓各模塊交互作用。<br />TimerServer排程器是系統核心中的核心,所有的元件執行都透過TimerServer,透過runner欄位,能讓TimerServer做到多主機跨網路的能力。</p><p><br /></p>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-38559424715257964752023-02-20T16:55:00.004+08:002023-02-20T17:03:10.817+08:00BladeX(SpringCloud)介紹前言<div><br /></div><div>這一兩年因為工作需要接觸了基於BladeX的Spring Cloud,深刻體會到Spring Cloud的博大精深,因為這是中國公司以Spring Cloud集成的微服務架構系統,台灣知道的人比較少,這邊就介紹下這個大傢伙。</div><div>另外,前一篇介紹了我對JavaScript和NodeJS的看法,這篇也會帶入,既然NodeJS這麼好,BladeX適用情境在哪?</div><div><br /></div><div>BladeX介紹</div><a href="#">BladeX</a>是由<a href="https://bladex.vip/#/">上海布雷德科技有限公司</a>集成的Spring Cloud解決方案,產品稱為<a href="https://www.blogger.com/blog/post/edit/10062204/3855942471525796475#">BladeX</a>。<div>BladeX包括開源版和商業版,不用懷疑,大家都用商業版,開源版基本上只用做入門,事實上以商業版而言,它的價格不算貴,商業版授權大概台幣30,000左右,無使用限制、無授權期限制、無升級版本限制,基本上就是買一次,無限使用。<br /><div>BladeX有2個版本,BladeX和BladeX Boot,兩個版本分別對應Spring Cloud和Spring Boot。</div><div>使用上,大部分使用者都使用BladeX,BladeX Boot版本用的人很少很少。</div><div><br /></div></div><div>BladeX系統需求</div><div>BladeX的架構是所謂的微服務2.0。</div><div>BladeX要佈署和運行,至少需要 MySQL、Nacos、Redis,其系統需求,除去MySQL、Redis、Nacos這3台伺服器外,通常還需要3台每台至少8GB(建議16GB)記憶體的伺服器。</div><div><br /></div><div>上述意思是,BladeX的運作環境,伺服器需求6台起跳。</div><div><br /></div><div>Spring Cloud與BladeX架構介紹</div><div>以Web Service觀點看Spring Cloud,Spring Cloud是目前Web後端微服務2.0架構的主流。</div><div>以前以SOAP Protocol為基礎的Java 2 Enterprise Edition (J2EE),目前都改以Spring架構實做。</div><div>單從Protocol觀點而言,J2EE使用SOAP為基礎,SOAP以RPC + HTTP + XML組成,Spring則以(WebSocket) + REST API + HTTP + json組成。</div><div>既然對比的是J2EE,Spring Cloud的特點是在高穩定、模塊化、大架構、分散式的企業級應用情境。</div><div>Spring Cloud由許多輕量級組件組合而成,我們從下面這個架構圖看起:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFFOTFAJy6nSIJ2fpfbYz5a5M5ndrCJrHHjGq0kXn9HKOyaozI5fHPyU2jSU_ymCnQUgZOGP-9cImWKnmbBio-DDq7cuSNjVfmAVkkF_4iF3S0wJkDNrIT9ok6nVHVJZ0DkuaEMmj9MyJ7OKmvM6Y72bdY8TxnEyPnS53DWN8UuIZ6KxrfzKA/s1024/365537-20180629225744020-1799598918.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="628" data-original-width="1024" height="393" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFFOTFAJy6nSIJ2fpfbYz5a5M5ndrCJrHHjGq0kXn9HKOyaozI5fHPyU2jSU_ymCnQUgZOGP-9cImWKnmbBio-DDq7cuSNjVfmAVkkF_4iF3S0wJkDNrIT9ok6nVHVJZ0DkuaEMmj9MyJ7OKmvM6Y72bdY8TxnEyPnS53DWN8UuIZ6KxrfzKA/w640-h393/365537-20180629225744020-1799598918.png" width="640" /></a></div><br /><div><br /></div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>來源:<a href="https://www.cnblogs.com/Irving/p/7598883.html">https://www.cnblogs.com/Irving/p/7598883.html</a></div><div><br /></div><div>這張架構圖算是比較簡潔的圖,先以這張圖描述服務註冊(Service Register)和服務發現(Service Discovery)。</div><div><br /></div><div>Spring Cloud的架構中,以服務註冊(Service Register)和服務發現(Service Discovery)為基礎,在Spring Cloud中,一般用Eureka (BladeX中是Nacos)。</div><div>所謂的服務註冊(Service Register),是系統中所有要運行的微服務,在加入系統時,都要先透過Eureka進行註冊(Service Register),服務註冊後,系統內要呼叫和使用服務時,都可以透過Eureka的服務發現(Service Discovery),自動的找出相對應服務的微服務。</div><div>例如:</div><div>上圖中右側有個Auth Service,是使用者認證用的,要在上圖系統中使用Auth Service</div><div>首先,要先在Eureka (BladeX是Nacos)中先註冊Auth Service</div><div>使用時,要使用的程式要先詢問Eureka (BladeX是Nacos),Auth Service是誰?Eureka (BladeX是Nacos)會將Auth Service的IP和Port回傳給要使用的程式。</div><div>這個註冊和使用過程看起來有點複雜,在Spring Cloud中,呼叫、查詢和使用封裝在Feign Client API當中,透過呼叫和使用Feign Client API,它底層直接將上述行為處理完成。</div><div><br /></div><div>在寫程式時,我們會需要全域變數紀錄和處理環境變數,並讓系統內所有程式都能存取,同樣的,Spring Cloud的微服務也需要存取共用的變數資料,在Spring Cloud中,並不將它稱為變數,而將它稱為Config或設置參數。</div><div>因為這些Config需要能夠共用,因此這些Config需要特定一個服務儲存和管理,它就是上述架構途中的Spring Cloud Config(BladeX中是Nacos)。</div><div>系統內所有的微服務,都能夠透過存取Spring Cloud Config(BladeX是Nacos),儲存和讀取Config。</div><div>例如:</div><div>微服務要能存取MySQL,通常會將MySQL的JDBC URL存放在Spring Cloud Config(BladeX是Nacos),使用時取出。</div><div><br /></div><div>前面提到過,BladeX是Spring Cloud集成的解決方案,在BladeX當中,上述的服務註冊(Service Register)、服務發現(Service Discovery)和Config參數管理,都是透過<a href="https://nacos.io/">Nacos</a>管理的,而Nacos是阿里巴巴針對Spring Cloud開發的輕量級服務。</div><div><br /></div><div>Spring Cloud中包含很多微服務,實際使用時,Client端通常不會直接存取微服務,Client端一般會透過Spring Cloud的API Gateway存取,在Spring Cloud中是ZUUL,而在BladeX中,它包含了一個稱為bladex gateway的微服務。</div><div>架構圖如下:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF-UQts2HuNEj9e-QdE0lLCXzYKFlnWV2CRQd2fH7E6JiFiTHu7ziFPBZzJrMeCGgiXpcX5uffA5iOtQY-GnlBTwfTz4pG55D2Y5HY_oNrRcUa-s5cvU1ndJNnbBnJW1upSksZVKLVLy71xJQey2D6XA2t2Fx3tncF1kr4ILJ_w2iAwgD5bN8/s561/zuul-api-gateway.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="519" data-original-width="561" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF-UQts2HuNEj9e-QdE0lLCXzYKFlnWV2CRQd2fH7E6JiFiTHu7ziFPBZzJrMeCGgiXpcX5uffA5iOtQY-GnlBTwfTz4pG55D2Y5HY_oNrRcUa-s5cvU1ndJNnbBnJW1upSksZVKLVLy71xJQey2D6XA2t2Fx3tncF1kr4ILJ_w2iAwgD5bN8/s16000/zuul-api-gateway.jpg" /></a></div><div class="separator" style="clear: both; text-align: center;">來源:<a href="https://exampledriven.wordpress.com/2016/07/06/spring-cloud-zuul-example/">https://exampledriven.wordpress.com/2016/07/06/spring-cloud-zuul-example/</a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>BladeX的bladex gateway和ZUUL作用相同。</div><div>所有Client都存取API Gateway,ZUUL和其他微服務的需求,則透過Feign Client API互相存取,中間存取的API,都是REST API,資料格式都是json。</div><div><br /></div><div>BladeX(Spring Cloud)與php/express/django比較</div><div>典型的Web應用,通常由SQL(常見MySQL)、Apache、Web Backend、Web Frontend組成。</div><div>通常SQL一台,Apache和Web Backend一台,其他Client端都在系統外部。</div><div>這樣的應用,一般使用php/nodejs express/python django都能方便開發,經典的CRUD這類都足夠並方便使用,我自己估計,大概9成的應用都屬於此。</div><div>BladeX與Spring Cloud適用在企業的大型Web應用,它最基本需要穩定,還需要模組化,能夠以模組形式開發新服務,還需要具備分散式系統的橫向擴充性。</div><div>例如:</div><div>當希望有多個Client入口分散系統負載時,直接開多個API Gateway即可,只要確定Nacos內API Gateway有多個Instance,就能橫向擴展服務。</div><div><br /></div><div>上述這些需求,會讓系統架構的複雜度變高,比較不適合直接用php/express/django開發,底層需要的API和SDK太多太雜。</div><div>舉個例子:</div><div>Nacos有NodeJS的API,透過呼叫Nacos API,NodeJS也能做到服務發現、服務註冊、Config存取,但這個只是Nacos API,並不是Feign Client API,實際在開發微服務時,需要大量使用Feign Client API存取其他微服務,光是要把NodeJS Nacos API包裝成Feign Client API,就要花費很多時間成本,而在BladeX中,Feign Client API只是其中一組常用的API,其他像是資料庫ORM的MyBatis API、加解密API...等,大概有10幾組API,不大可能全部用NodeJS重造輪子。</div><div><br /></div><div>結論</div><div>BladeX是博大精深的微服務Web Framework,它集成了認證、會員管理、後台管理界面...等功能,它的系統集成了以Nacos為基礎的許多輕量級Spring Cloud組件服務,且能夠擴展支援包括Prometheus...等監控系統服務,承襲Spring Cloud的架構、模組化設計、橫向擴充能力、服務管理...等特點,讓大型Web應用的開發變得容易。</div><div>同時,BladeX與Spring Cloud這類Web Framework的設計目標,並不是使用在常見的小型Web應用,如果開發的目標項目是一般會員網站或功能網站,系統需求鎖定在2台以下的Web Server時,以express/django開發可能更為簡單方便。</div><div>(圖與排版後續補上)</div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-36861911994545912182023-02-20T12:30:00.001+08:002023-02-20T12:41:37.123+08:00JavaScript/NodeJS Web前端之我見<p>前言</p><p>這一兩年因為工作需要接觸了JavaScript/NodeJS與Web前端,也因此對所謂的Web前端有了更深刻的體會和理解。</p><p>這篇文章雖然標題寫的是JavaScript介紹,但後面會描述JavaScript和Web前端的不可替代性。</p><p>Web前後端概念</p><p>早期的Web並沒有區分前後端,典型的開發方式是先做個Web版型,然後Web內容替換asp/php/jsp寫的function和變數,瀏覽器載入時,Server會執行這些function,並將執行結果放入Web中提供給瀏覽器。</p><p>目前的Web開發,則多半會建議前後端分離,所謂的前後端分離,指的是後端一樣是asp/php/jsp,現在甚至可以是NodeJS Express/Python Django。</p><p>後端開發後,提供的是所謂的RESTFul API,一般簡稱REST API或API。</p><p>前端開發,則幾乎都是以JavaScript/TypeScript為主的Web App。</p><p>為什麼分成前後端?有沒有必要?能不能不學JavaScript/TypeScript而用Java/Python/C++開發?</p><p>這是我在接觸Web前端時的一系列疑問,而後得到了答案。</p><p>1. 為什麼分成前後端?有沒有必要?</p><p>Google多半會查到,現在的趨勢如此,專業分工,前後端架構...等,但WordPress沒有前後端分離,一樣有很大的使用群,那需求和原因在哪?</p><p>其實最關鍵的需求和原因,是多平台支援。</p><p>現在這個時代,手機佔了很大的使用群,手機又區分了Apple和Android,Apple要使用ObjectC/Swift開發,Android要使用Java/Kotlin開發,而傳統的PC和Web要支援嗎?PC可能要用c#開發,Web要使用JS開發。</p><p>為了能夠滿足多平台開發,典型前後端一體的開發方式,無法適用在這樣的情境中,因此,將後端改為REST API,前端各自使用HTTP API串接成了解決方案,而這點,才是前後端分離開發的真正原因。</p><p>換句話說,假設今天明確的需求是,我們只使用Web瀏覽器,並且不打算支援手機,那麼是否要前後端分離其實沒這麼重要。</p><p>2. 能不能不學JavaScript/TypeScript而用Java/Python/C++開發?</p><p>這個問題,很多後端想跨入前端開發的都會問,我一開始也是如此,事實上這有好處。</p><p>想想看,團隊成員都會Java,直接用Java開發前後端,人力能自由調配,這是很大的優勢。</p><p>但實際上呢?答案是不太行,原因很簡單。</p><p>目前所有的Web瀏覽器,核心都是JavaScript Engine,而目前Web瀏覽器,尤其是Chrome,基本上跨所有作業系統平台,Chrome地位等同以前的IE,幾乎是Web瀏覽器的代名詞,因為它原生支援的是JavaScript Engine,學習JavaScript這件事就避免不了。</p><p>那TypeScript和其他語言又如何呢?</p><p>目前TypeScript和其他語言,都是透過轉譯器的方式,將程式碼轉譯成JS後,在提供給Web瀏覽器執行的。</p><p>Web前端優勢</p><p>我們先帶個比較無關的東西,看一看Google AI的TensorFlow SDK"主要"支援哪些語言?</p><p><a href="https://www.tensorflow.org/api_docs">TensorFlow API Document</a><br /></p><p>TensorFlow SDK支援4種語言,包括:</p><p>Python/JavaScript/C++/Java</p><p>其中有趣的是,Java點進去會發現,它都寫這個支援未來可能移除。</p><p>Python因為簡單易學,是目前的顯學,學校教的程式語言,都開始改教Python,以AI主軸在類神經網路設計/訓練/推論這幾個開發點,程式語言帶出的系統架構不是重點,程式執行速度也不是重點,簡單實做和修改類神經網路才是重點,因此以Python為主要語言是合理的。</p><p>C++的優勢除了程式執行速度快,上至伺服器服務和Web後端,下至MCU都能支援,最重要的是,C++編譯後,Binary反組譯比較難,原始程式碼隱蔽性好,是主要語言合理。</p><p>Java在程式語言開發中,仍舊有非常高的使用率,因此支援Java不意外,但它的開發便利性不如Python,程式碼沒有隱蔽性,不如C++,因此有支援但可能移除就不意外了。</p><p>那JavaScript為什麼變成主要支援的語言?</p><p>簡單的說,目前所有Web瀏覽器都只支援JavaScript,而目前所有作業系統平台都有Web瀏覽器,支援JavaScript,等於能讓AI應用從PC到手機全部支援。</p><p>意思是,JavaScript與Web瀏覽器,原生就具備跨平台能力,儘管在不同平台上的整合度沒有各平台原生語言強,但套用Java以前的標語:One language run anywhere 完全沒問題。</p><p>JavaScript與分散式特性</p><p>傳統系統開發時,為了最大化系統效能,通常會測試系統可支持的最大負載量,然後將最大負載量設為80%左右。</p><p>一般而言,不論是PC/IPC/開發板,效能越強單價越高,通常都希望盡可能的降低程式CPU和記憶體的使用量,拉高系統可負載量。</p><p>當我們代入Web前後端開發後,上述的最大負載量能直覺反應,它是屬於Web後端的服務,那前端呢?</p><p>Web前端通常運作在Web瀏覽器中,因此JavaScript和Web瀏覽器執行在Client端。</p><p>一個極端的說法,對於Web前端而言,後端只需要有個Web Server提供儲存空間,能讓Web瀏覽器下載HTML和JS,後續就能在Client端的瀏覽器中執行。</p><p>從這個觀點看JavaScript和Web前端,它是完美的分散式架構,所有程式都分散運行在全部的Client當中,不會佔用後端Server服務資源。</p><p>白話的說,如果盡可能在Web前端用JS開發和執行,能最大程度降低後端服務器的負載量,增加整個系統的使用效率。</p><p>而目前Web瀏覽器和JS SDK能夠支援的運算越來越多,已不是傳統的JS function只處理頁面顯示,像是TensorFlow能夠直接用Web瀏覽器在本地執行Ai程式,WebGL能夠直接在Web瀏覽器繪圖,瀏覽器內嵌的影片播放器能夠直接播放影片並進行H264/H265...等影像格式解碼,以往這些負載較大的程式,現在都能轉到Web前端運行,以分散式觀點而言,能很大程度的降低後端負載,增加系統負載量。</p><p>Web3.0與區塊鍊</p><p>區塊鍊細節這裡不提,一方面熟悉度沒這麼高,一方面是要描述Web3.0與分散式發展的觀點。</p><p>前面提到,從Web前後端觀點看JavaScript和Web瀏覽器的應用,JavaScript和Web形成了完美的分散式架構,有沒有一種可能,讓全部的Web瀏覽器之間互相連線,運行同一支JavaScript程式,變成一個全分散的執行環境和系統?</p><p>我認為,Web3.0提出的區塊鍊就是答案。</p><p>我們把記憶回到以前的P2P,包括eDonkey和BT,並以eDonkey設計思考。</p><p>eDonkey這類P2P的典型設計如下:</p><p>所有P2P Client都有檔案清單,所有資源都在P2P Client,eDonkey Server處理的,是紀錄和提供P2P Client的IP列表。</p><p>P2P Client啟動後,先連上eDonkey Server詢問最新的P2P Client列表,再跟自己的P2P Client列表比對,接著根據上傳或下載提供檔案(資源)清單,接著進行檔案(資源)上傳和下載。</p><p>Server主要服務是維護P2P Client清單。</p><p>BT則進一步透過DHT,讓P2P Client之間能夠互相連線取得這份清單,進一步降低Server的依賴性。</p><p>那麼,有沒有可能這樣的設計用JavaScript開發在Web瀏覽器上面執行?</p><p>我認為區塊鍊是這樣設計的變形。</p><p>典型的區塊鍊,每個Client(錢包)都以token形式表示,Client(錢包)中每個代幣(資源)也都以token表示,每個所謂的主鍊(P2P Network),所有的代幣(資源)都以linklist的形式串接,新的代幣(資源)要加入到主鍊(P2P Network),需要主鍊上超過一半以上的Client認可後,串入其中。</p><p>會發現,Server在其中的角色和傳統P2P網路的Server有一點相似,Server所謂的交易處理,可看做傳統P2P網路中,新增檔案(資源)時,通知各個P2P Client,進行資源清單同步的動作。</p><p>當然,在區塊鍊下,區塊鍊的Server的服務更多,區塊鍊錢包的功能是以錢包為主,沒有所謂的資源,但如果以P2P和Web瀏覽器的分散性思考,比較能夠體會坊間將區塊鍊介紹為Web3.0的原因。</p><p><br /></p><p>結論</p><p>JavaScript從一開始就是Web瀏覽器主要的執行引擎,在目前的Web開發中,具有Web瀏覽器執行的不可取代性。</p><p>因為Web的普及,Web瀏覽器是很重要的跨平台環境,這讓JavaScript具有特殊性。</p><p>又因為Web瀏覽器是在Client端作業系統中運行,讓JavaScript具有分散性。</p><p>以前Java所謂的One language run anywhere,現在的作業系統中,Windows Linux PC預設都沒有Java,Apple iOS也沒有Java,只有Android還以JavaVM為基礎。</p><p>但看看現在的Web瀏覽器,PC/Android/Apple iOS,每個平台都有,JavaScript在這個時代成為了One language run anywhere的代表。</p><p>以此為基礎,NodeJS可單獨執行,並且有Express這樣的Framework,這讓JavaScript從Web瀏覽器前端跨入伺服器後端服務程式,回想一開始期望的,假如團隊中都是Java開發者,前端到後端都使用Java開發,人力調配的便利性,如果Java改成JavaScript和NodeJS呢?</p><p>(圖和排版後續補上)</p>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-27044985865649453922021-04-05T13:12:00.003+08:002021-04-05T13:24:15.027+08:00RaspberryPi Pico FreeRTOS Timer用Java/Qt Timer Style實作<p>前言:</p><div style="text-align: left;">我將FreeRTOS的Timer封裝,使用方式和Java或Qt的用法相似,好方便使用。</div><p>用法:</p><div style="text-align: left;">1. 參考Thread用法實作Class</div><div style="text-align: left;"><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: purple;">Runner</span><span style="color: silver;"> </span>*<span style="color: #092e64;">runnerA</span><span style="color: silver;"> </span>=<span style="color: silver;"> </span><span style="color: olive;">new</span><span style="color: silver;"> </span><span style="color: purple;">Runner</span>(<span style="color: navy;">1</span>);</pre></div><div style="text-align: left;"><br /></div><div style="text-align: left;">2. 實作Timer物件</div><div style="text-align: left;">每個Timer和Object實作一個Timer物件</div><div style="text-align: left;"><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: purple;">CTimer</span><span style="color: silver;"> </span>*<span style="color: #092e64;">timer1</span><span style="color: silver;"> </span>=<span style="color: silver;"> </span><span style="color: olive;">new</span><span style="color: silver;"> </span><span style="color: purple;">CTimer</span>(<span style="color: green;">"runnerA"</span>,<span style="color: silver;"> </span><span style="color: #092e64;">runnerA</span>);</pre></div><div style="text-align: left;"><br /></div><div style="text-align: left;">3. 設定Timer</div><div style="text-align: left;"><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: #092e64;">timer1</span>-><span style="color: #00677c;">SetTimer</span>(<span style="color: navy;">5</span>,<span style="color: silver;"> </span><span style="color: olive;">true</span>);</pre></div><div style="text-align: left;"><br /></div><div style="text-align: left;">參數1: 定時時間(單位是FreeRTOS的tick)</div><div style="text-align: left;">參數2: 是否循環觸發</div><div style="text-align: left;"><br /></div><div style="text-align: left;">4. 啟動Timer</div><div style="text-align: left;"><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: #092e64;">timer1</span>-><span style="color: #00677c;">Start</span>();</pre></div><div style="text-align: left;"><br /></div><div style="text-align: left;">程式碼:</div><p><a href="https://gitlab.com/ycfunet/freertos_timer_template" target="_blank">https://gitlab.com/ycfunet/freertos_timer_template</a><br /></p><p> </p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-16483735132089667682021-04-05T11:17:00.007+08:002021-04-05T13:24:31.219+08:00RaspberryPi Pico FreeRTOS Task用Java/Qt Thread Style實作<p>前言:</p><div style="text-align: left;">我將FreeRTOS的Task用Thread方式實作,使用方式和Java或Qt的用法相似,好處是使用上比較方便習慣些。</div><p>用法:</p><div style="text-align: left;">1. 繼承使用Thread</div><div style="text-align: left;"><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: navy;">#include</span><span style="color: silver;"> </span><span style="color: green;">"pico/stdlib.h"</span></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: navy;">#include</span><span style="color: silver;"> </span><span style="color: green;">"thread.h"</span></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: olive;">class</span><span style="color: silver;"> </span><span style="color: purple;">LedWriter</span><span style="color: silver;"> </span>:<span style="color: silver;"> </span><span style="color: olive;">public</span><span style="color: silver;"> </span><span style="color: purple;">Thread</span></pre><pre style="margin-bottom: 0px; margin-top: 0px;">{</pre><pre style="margin-bottom: 0px; margin-top: 0px;">};</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;">2. 實做run Method</pre></div><div style="text-align: left;"><pre class="code highlight" lang="cpp"><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: olive;">public</span>:</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span><span style="color: purple;">LedWriter</span>();</pre></pre><pre class="code highlight" lang="cpp">.....
<span class="line" id="LC13" lang="cpp"> </span>void<span style="color: silver;"> </span><span style="color: #00677c; font-style: italic; font-weight: 600;">run</span>();</pre><pre class="code highlight" lang="cpp"><span class="line" lang="cpp"><span class="p">.....</span></span></pre></div><div style="text-align: left;">3. 主程式中實做</div><div style="text-align: left;"><pre class="code highlight" lang="cpp"><span class="line" id="LC11" lang="cpp"><span class="n">T</span>askHandle_t<span style="color: silver;"> </span><span style="color: #092e64;">ledwriter_thread</span><span style="color: silver;"> </span>=<span style="color: silver;"> </span><span style="color: navy;">NULL</span>;<br /></span><span class="line" id="LC13" lang="cpp"><span class="n">LedWriter<span style="color: silver;"> </span>*<span style="color: #092e64;">led_writer</span><span style="color: silver;"> </span>=<span style="color: silver;"> </span><span style="color: olive;">new</span><span style="color: silver;"> </span><span style="color: purple;">LedWriter</span>()</span></span>;</pre></div><div style="text-align: left;"><br /></div><div style="text-align: left;">4. xTaskCreate啟動Task</div><pre class="code highlight" lang="cpp">BaseType_t ret<span style="color: silver;"> </span>=<span style="color: silver;"> </span>xTaskCreate(<span style="color: purple;">LedWriter</span>::<span style="color: maroon;">start</span>,<span style="color: silver;"> </span><span style="color: green;">"led_writer"</span>,<span style="color: silver;"> </span><span style="color: navy;">512</span>,<span style="color: silver;"> </span><span style="color: #092e64;">led_writer</span>,<span style="color: silver;"> </span>tskIDLE_PRIORITY,<span style="color: silver;"> </span>&<span style="color: #092e64;">ledwriter_thread</span>);</pre><p>程式碼下載:</p><p><a href="https://gitlab.com/ycfunet/freertos_thread_template" target="_blank">https://gitlab.com/ycfunet/freertos_thread_template</a><br /></p><p><br /></p>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-13097704444455359282021-03-30T21:04:00.000+08:002021-03-30T21:04:01.664+08:00RaspberryPi Pico FreeRTOS Debug測試<p>前言:</p><div style="text-align: left;">Microcontroller如果同時多個感測器,複雜度會提高,當複雜度比較高時,就需要分時多工,作法上通常是寫或套用個Scheduler,或者往上一階放個RTOS上去,放OS系統複雜度比較高,但提供的功能和彈性比較大。</div><div style="text-align: left;">RTOS當中,目前的主流大概是FreeRTOS,一方面它的Licence是MIT,另一方面它支援的Microcontroller比較多,Plugin也比較多,因此找Pico的FreeRTOS嘗試看看。</div><div style="text-align: left;">因為Microcontroller比較複雜,更需要UI的Debugger協助,除錯或開發才會比較有效率。</div><p>環境:</p><div style="text-align: left;">Host: Ubuntu 18.04</div><div style="text-align: left;">SWD Adapter: RaspberryPi 4b</div><div style="text-align: left;">Target: RaspberryPi Pico</div><div style="text-align: left;">FreeRTOS git: <a href="https://github.com/PicoCPP/RPI-pico-FreeRTOS">https://github.com/PicoCPP/RPI-pico-FreeRTOS</a></div><p>安裝設置 (Host端):</p><div style="text-align: left;">Git下載</div><div style="text-align: left;"># cd /tmp</div><div style="text-align: left;"># git clone https://github.com/PicoCPP/RPI-pico-FreeRTOS</div><div style="text-align: left;"># cd RPI-pico-FreeRTOS</div><div style="text-align: left;"># git clone https://github.com/raspberrypi/pico-sdk</div><div style="text-align: left;"># git clone https://github.com/FreeRTOS/FreeRTOS-Kernel</div><div style="text-align: left;"><br /></div><p>QtCreator編譯FreeRTOS專案:</p><div style="text-align: left;">1. 開啟專案</div><div style="text-align: left;">用QtCreator開啟 /tmp/RPI-pico-FreeRTOS/CMakeLists.txt</div><div style="text-align: left;"><br /></div><div style="text-align: left;">2. 修改CMakeLists.txt新增Debug</div><div style="text-align: left;">.....</div><div style="text-align: left;"><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: olive;">set</span>(<span style="color: #092e64;">CMAKE_BUILD_TYPE</span><span style="color: silver;"> </span>Debug)</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><div style="font-family: "Times New Roman"; white-space: normal;">.....</div><div style="font-family: "Times New Roman"; white-space: normal;"><br /></div><div style="font-family: "Times New Roman"; white-space: normal;">3. 建置</div><pre style="margin-bottom: 0px; margin-top: 0px;"><div style="font-family: "Times New Roman"; white-space: normal;"><div>Rescan Project</div><div>全部清除</div><div>清理專案 "hello_world"</div><div>全部建置</div></div></pre></pre></div><p>FreeRTOS除錯:</p><div style="text-align: left;"><div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><a href="https://lh3.googleusercontent.com/-YwD8Yc2To7w/YGMhqryYfrI/AAAAAAAAwnY/d3ud94r9VTkV_p1Rfnhg7JiwnxmXJ2n1wCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="740" data-original-width="1366" height="365" src="https://lh3.googleusercontent.com/-YwD8Yc2To7w/YGMhqryYfrI/AAAAAAAAwnY/d3ud94r9VTkV_p1Rfnhg7JiwnxmXJ2n1wCLcBGAsYHQ/w675-h365/image.png" width="675" /></a></div><br /><br /></div><p><br /></p><p><br /></p>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-65174096769947598022021-03-30T16:53:00.000+08:002021-03-30T16:53:00.744+08:00RaspberryPi Pico C/C++ Debug (QtCreator篇)<p>前言:</p><div style="text-align: left;">這邊延續前面CLI篇的說明,OpenOCD的設置已經做過了,這邊直接設定QtCreator。</div><p>環境:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-LQC7NTuk_1s/YGLOoQRY17I/AAAAAAAAwk4/Y8Sv3N66bqw1zwkCxWdHzTHe3q3cwUf9ACPcBGAYYCw/s800/Pico%2BDebug%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25961.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="451" data-original-width="800" height="385" src="https://1.bp.blogspot.com/-LQC7NTuk_1s/YGLOoQRY17I/AAAAAAAAwk4/Y8Sv3N66bqw1zwkCxWdHzTHe3q3cwUf9ACPcBGAYYCw/w680-h385/Pico%2BDebug%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25961.png" width="680" /></a></div><p></p><p><br /></p><p>QtCreator設定:</p><div style="text-align: left;">1. 參考下圖進入設置頁面</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-5yJCIGTz3Ng/YGLfnYyp2NI/AAAAAAAAwlk/lHpNZ4tDF6Upl4SV_cnC1WSH9cPtGEioACLcBGAsYHQ/s993/1.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="571" data-original-width="993" height="386" src="https://1.bp.blogspot.com/-5yJCIGTz3Ng/YGLfnYyp2NI/AAAAAAAAwlk/lHpNZ4tDF6Upl4SV_cnC1WSH9cPtGEioACLcBGAsYHQ/w671-h386/1.png" width="671" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-UQ49i5da0-M/YGLfxsTo8CI/AAAAAAAAwlo/HI3QFLmCdFoemUGgt4T_pRPzqzJB6gG8QCLcBGAsYHQ/s796/2.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="534" data-original-width="796" height="454" src="https://1.bp.blogspot.com/-UQ49i5da0-M/YGLfxsTo8CI/AAAAAAAAwlo/HI3QFLmCdFoemUGgt4T_pRPzqzJB6gG8QCLcBGAsYHQ/w675-h454/2.png" width="675" /></a></div><div>2. 設定Debugger</div><div>先設定ARM Debugger (需先安裝gdb-multiarch)</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-6vRoElvRagk/YGLgTcOLAhI/AAAAAAAAwl0/SVWpm8QPVmoyYFimE52l1gG1Hj9rR2nrwCLcBGAsYHQ/s1068/4.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="610" data-original-width="1068" height="385" src="https://1.bp.blogspot.com/-6vRoElvRagk/YGLgTcOLAhI/AAAAAAAAwl0/SVWpm8QPVmoyYFimE52l1gG1Hj9rR2nrwCLcBGAsYHQ/w674-h385/4.png" width="674" /></a></div><br /><div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/--_EIcobIqj4/YGLgaggJ2bI/AAAAAAAAwl4/hz65V2IgMjAZ4OQ6ksg1LK3mEtWZis9TgCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="610" data-original-width="1068" height="386" src="https://lh3.googleusercontent.com/--_EIcobIqj4/YGLgaggJ2bI/AAAAAAAAwl4/hz65V2IgMjAZ4OQ6ksg1LK3mEtWZis9TgCLcBGAsYHQ/w675-h386/image.png" width="675" /></a></div><br /><br /></div><div>設置內容如下:<br /></div><div>Name: ARM Debugger</div><div>Path: /usr/bin/gdb-multiarch</div><div><br /></div><div>3. 設置Kits</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-tyHpKbwno-U/YGLhN1w3rCI/AAAAAAAAwmE/BxawBPoFi60t5kO9h6fBcSULkXwCpot3QCLcBGAsYHQ/s1068/3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="610" data-original-width="1068" height="387" src="https://1.bp.blogspot.com/-tyHpKbwno-U/YGLhN1w3rCI/AAAAAAAAwmE/BxawBPoFi60t5kO9h6fBcSULkXwCpot3QCLcBGAsYHQ/w677-h387/3.png" width="677" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><a href="https://lh3.googleusercontent.com/-zUUsNYY3kDQ/YGLisVf2U6I/AAAAAAAAwmU/U2tL1XyhhhU_GaCpCy4cf_6VHhCa0avKwCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="610" data-original-width="1075" height="386" src="https://lh3.googleusercontent.com/-zUUsNYY3kDQ/YGLisVf2U6I/AAAAAAAAwmU/U2tL1XyhhhU_GaCpCy4cf_6VHhCa0avKwCLcBGAsYHQ/w680-h386/image.png" width="680" /></a></div><br /><br /></div><br /><div>設置內容如下:</div><div>名稱: Pico</div><div>裝置型態: 通用 Linux 裝置</div><div><div>Compiler: C: GCC (C, arm 32bit in /usr/bin)</div><div><div>Compiler: C++: GCC (C++, arm 32bit in /usr/bin)</div><div>Debugger: ARM Debugger (前面Debugger設定的名稱)</div><div>Qt 版本: 無</div></div></div><div><br /></div><div>4. 設置Linux裝置</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-w1iDqRNd4Is/YGLjYwQ118I/AAAAAAAAwmc/V3fx9INaltsbGJ-xiUPKsRpHmAeJROobgCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="579" data-original-width="1067" height="366" src="https://lh3.googleusercontent.com/-w1iDqRNd4Is/YGLjYwQ118I/AAAAAAAAwmc/V3fx9INaltsbGJ-xiUPKsRpHmAeJROobgCLcBGAsYHQ/w673-h366/image.png" width="673" /></a></div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-ARqd1Adpyd0/YGLkXlhdEzI/AAAAAAAAwmo/6kJfY_gaiVgun1yUUDmRn0mNEOLqQ6lOACLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="610" data-original-width="1075" height="385" src="https://lh3.googleusercontent.com/-ARqd1Adpyd0/YGLkXlhdEzI/AAAAAAAAwmo/6kJfY_gaiVgun1yUUDmRn0mNEOLqQ6lOACLcBGAsYHQ/w677-h385/image.png" width="677" /></a></div><br /><br /></div><br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-tayJTj2-d0s/YGLkxWHqvZI/AAAAAAAAwm0/FHFJr1_-tCg36iN1oBCifHfOAnCNR6sTgCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="612" data-original-width="1077" height="384" src="https://lh3.googleusercontent.com/-tayJTj2-d0s/YGLkxWHqvZI/AAAAAAAAwm0/FHFJr1_-tCg36iN1oBCifHfOAnCNR6sTgCLcBGAsYHQ/w675-h384/image.png" width="675" /></a></div><br />裝置名稱: Pico SWD Adapter</div><div>Pi 4b SWD Adapter IP: 192.168.1.35</div><div>username: root</div><div>使用者密碼: root密碼 (Pi 4b要設定sshd_config,PermitRootLogin yes)<br /><br /></div><div>5. Kits設定Linux裝置為上面的Pico SWD Adapter</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-YttFpCQfNJY/YGLlm-jrc8I/AAAAAAAAwm8/YIwvAvLmP1YlAaahoEZ38oguPktXFf6rgCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="612" data-original-width="1077" height="382" src="https://lh3.googleusercontent.com/-YttFpCQfNJY/YGLlm-jrc8I/AAAAAAAAwm8/YIwvAvLmP1YlAaahoEZ38oguPktXFf6rgCLcBGAsYHQ/w671-h382/image.png" width="671" /></a></div><br /><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /><div><br /></div><div><br /></div><br /><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><br /><div style="text-align: left;"><br /></div><p>開始Debug</p><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/--uz9X9V-FNI/YGLmKt_xXgI/AAAAAAAAwnE/GC6wJYLlj_kvJLsRk5bq5zr7mJr5b9dogCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="645" data-original-width="1016" height="434" src="https://lh3.googleusercontent.com/--uz9X9V-FNI/YGLmKt_xXgI/AAAAAAAAwnE/GC6wJYLlj_kvJLsRk5bq5zr7mJr5b9dogCLcBGAsYHQ/w684-h434/image.png" width="684" /></a></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Kits: Pico</div><div style="text-align: left;">Server Port: 3333</div><div style="text-align: left;">本地執行檔: 選取build路徑下的.elf檔案 (piblink.elf)</div><div style="text-align: left;">Break at "main": v</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><a href="https://lh3.googleusercontent.com/-6KolLilDrl0/YGLm2xtEFlI/AAAAAAAAwnM/LvIQOkuexgkm39-afl5oNd8sSh1p796aQCLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="644" data-original-width="1170" height="365" src="https://lh3.googleusercontent.com/-6KolLilDrl0/YGLm2xtEFlI/AAAAAAAAwnM/LvIQOkuexgkm39-afl5oNd8sSh1p796aQCLcBGAsYHQ/w664-h365/image.png" width="664" /></a></div><br /><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><br /><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><p><br /></p><p><br /></p><p><br /></p></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-61273785351615367652021-03-30T16:05:00.005+08:002021-04-02T17:29:26.728+08:00RaspberryPi Pico C/C++ Debug (CLI篇)<p>前言:</p><div style="text-align: left;">RaspberryPi Pico有提供SWD界面(前身是JTAG),能透過SWD Adapter除錯。</div><div style="text-align: left;">身為老古董,以前用JTAG,需要一個JTAG Adapter,現在時代進步到SWD了,以前的JTAG Adapter同樣升級,現在是SWD Adapter,對老古董而言,特別的是以前JTAG Adapter比較貴,而且可能需要個ARM的程式提供Driver和Protocol,現在有OpenOCD,OpenOCD是OpenSource,而現在的RaspberryPi Zero/2b/3b/4b都能用GPIO模擬SWD Adapter,甚至RaspberryPi Pico也有picoprobe,直接放韌體,就能讓Pico當SWD Adapter。</div><div style="text-align: left;">因為是延續JTAG,因此現在主流的Microcontroller看起來都支援SWD,常見的除了新出的Pico之外,STM32也是常見的平台。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">因為Debug有點複雜,因此分2篇,這篇做的是OpenOCD環境建置以及gdb指令除錯。</div><div style="text-align: left;">也許會說,那我直接跳下一篇,直接看QtCreator呢?</div><div style="text-align: left;">不太建議,因為設定和建置都在這篇,QtCreator的設定基礎,也都是gdb,這篇實做後,才能銜接上。</div><div style="text-align: left;">SWD Adapter的部份,這篇先用RaspberryPi 4b,原因是,OpenOCD RaspberryPi的設定,預設是RaspberryPi 4b,如果要用其他版本,要修改記憶體位址(memory address)、時脈(clock)、GPIO號碼(GPIO number)。</div><p>環境:</p><div style="text-align: left;">Host: Ubuntu 18.04</div><div style="text-align: left;">SWD Adapter: Raspberry Pi 4b</div><div style="text-align: left;">Target: RaspberryPi Pico</div><p>接線示意圖:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-LQC7NTuk_1s/YGLOoQRY17I/AAAAAAAAwk0/jZHmofewPfYOjqVKtBCRCW96Pv7vAetsACLcBGAsYHQ/s800/Pico%2BDebug%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25961.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="451" data-original-width="800" height="380" src="https://1.bp.blogspot.com/-LQC7NTuk_1s/YGLOoQRY17I/AAAAAAAAwk0/jZHmofewPfYOjqVKtBCRCW96Pv7vAetsACLcBGAsYHQ/w673-h380/Pico%2BDebug%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25961.png" width="673" /></a></div><br /><div style="text-align: left;"><br /></div><p><br /></p><p><br /></p><p><br /></p><p>Pi OpenOCD環境安裝:</p><div style="text-align: left;">1. RaspberryPi OS安裝</div><div style="text-align: left;">先按照官方說明安裝Raspberry PI OS Lite</div><div style="text-align: left;">設置SSH</div><div style="text-align: left;">WiFi,我這邊設定WiFi為固定IP(192.168.1.35)</div><div style="text-align: left;"><br /></div><div style="text-align: left;">2. OpenOCD安裝</div><div style="text-align: left;">2a. 安裝套件</div><div style="text-align: left;"># apt-get update</div><div style="text-align: left;"># apt install git gdb-multiarch</div><div style="text-align: left;"># apt install automake autoconf build-essential texinfo libtool libftdi-dev libusb-1.0-0-dev</div><div style="text-align: left;"><br /></div><div style="text-align: left;">PS1. 下載OpenOCD Source Code要用git</div><div style="text-align: left;">PS2. Cross GDB執行檔現在直接包成gdb-multiarch套件,能直接支援Pico Binary format</div><div style="text-align: left;"><br /></div><div style="text-align: left;">2b. 下載OpenOCD</div><div># git clone https://github.com/raspberrypi/openocd.git --recursive --branch rp2040 --depth=1</div><div><br /></div><div>2c. 編譯 & 安裝OpenOCD</div><div># cd openocd</div><div># ./bootstrap</div><div># ./configure --enable-ftdi --enable-sysfsgpio --enable-bcm2835gpio</div><div># make -j8</div><div># make install</div><div><br /></div><div>2d. 安裝完成</div><div>安裝後檔案位置如下:</div><div>OpenOCD Execute file => /usr/local/bin/</div><div>OpenOCD Configure files => /usr/local/share/openocd/scripts/</div><div>RaspberryPi OpenOCD cfg => /usr/local/share/openocd/scripts/interface/raspberrypi-swd.cfg</div><div>RaspberryPi Pico(RP2040) Target cfg => /usr/local/share/openocd/scripts/target/rp2040.cfg</div><p>3. Host端GDB安裝</p><div style="text-align: left;">因為OpenOCD提供telnet、gdbserver、tcl除錯需要的3個界面,因此Host需要安裝:</div><div style="text-align: left;"># apt install gdb-multiarch telnet</div><div style="text-align: left;"><br /></div><div style="text-align: left;">4. Pico設置Debug模式</div><div style="text-align: left;">4a. 按下圖步驟將Pico上電</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-r-3o1DgujRk/YGLWjReykBI/AAAAAAAAwk8/K8sZrGPG4pgrAxHq-fjAGyQ5ECVVqFgawCLcBGAsYHQ/s468/Pico%2BDebug%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25962.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="358" data-original-width="468" height="515" src="https://1.bp.blogspot.com/-r-3o1DgujRk/YGLWjReykBI/AAAAAAAAwk8/K8sZrGPG4pgrAxHq-fjAGyQ5ECVVqFgawCLcBGAsYHQ/w673-h515/Pico%2BDebug%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25962.png" width="673" /></a></div><br /><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;">4b. Pi 4b執行OpenOCD</div><div style="text-align: left;"># openocd -f interface/raspberrypi-swd.cfg -f target/rp2040.cfg -c 'bindto 0.0.0.0'</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><a href="https://lh3.googleusercontent.com/-VaeaKBqyd_c/YGLYBmxuJWI/AAAAAAAAwlI/vTO3CuFhVPcrHFNg8Q5KTCb9EtvyX2n1QCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img alt="" data-original-height="521" data-original-width="1035" height="339" src="https://lh3.googleusercontent.com/-VaeaKBqyd_c/YGLYBmxuJWI/AAAAAAAAwlI/vTO3CuFhVPcrHFNg8Q5KTCb9EtvyX2n1QCLcBGAsYHQ/w674-h339/image.png" width="674" /></a></div><div style="text-align: left;"><br /></div><div style="text-align: left;">PS1. OpenOCD預設binding在本機(localhost),要加上參數 'bindto 0.0.0.0'</div><div style="text-align: left;">PS2. 如果出現錯誤訊息,請確定是參照4a步驟,按著按鈕插USB Cable (上電)</div><p>5. Host上用gdb除錯</p><p>QtCreator會Build在不同目錄,專案目錄「/tmp/my_pico_cpp_test」,檔案會產生在「/tmp/build-my_pico_cpp_test-unknown-Default」。</p><p>根據CMakeLists.txt設定,Pico燒錄用的檔案是uf2,Debug用的檔案則是elf,因此這裡要使用的是「piblink.elf」。</p><div style="text-align: left;">Debug執行步驟如下:</div><div style="text-align: left;">5a. 執行gdb</div><div style="text-align: left;"># gdb-multiarch /tmp/build-my_pico_cpp_test-unknown-Default/piblink.elf</div><div style="text-align: left;"><br /></div><div style="text-align: left;">5b. 連線到OpenOCD並載入piblink.elf</div><div style="text-align: left;">(gdb) target remote 192.168.1.35:3333</div><div style="text-align: left;">(gdb) load</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-e5MH1tQD8zU/YGLbP8xPSUI/AAAAAAAAwlU/kCxr1sJkQDc5NGStzGy_pjn5ToMZENG1ACLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="740" data-original-width="1366" height="365" src="https://lh3.googleusercontent.com/-e5MH1tQD8zU/YGLbP8xPSUI/AAAAAAAAwlU/kCxr1sJkQDc5NGStzGy_pjn5ToMZENG1ACLcBGAsYHQ/w675-h365/image.png" width="675" /></a></div><br /><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;">5c. 設置中斷點在main並執行</div><div style="text-align: left;">(gdb) b main</div><div style="text-align: left;">(gdb) continue</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-ZNytGZCEgf4/YGLbomuPYJI/AAAAAAAAwlc/JKcMCTeRKSQ9KwNJ3QeCFaY5eFxsOnyAwCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="261" data-original-width="773" height="228" src="https://lh3.googleusercontent.com/-ZNytGZCEgf4/YGLbomuPYJI/AAAAAAAAwlc/JKcMCTeRKSQ9KwNJ3QeCFaY5eFxsOnyAwCLcBGAsYHQ/w675-h228/image.png" width="675" /></a></div><br /><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><p style="text-align: left;">補充:</p><div style="text-align: left;">如果要使用Pi Zero (Pi 1)或者Pi 2b當作OpenOCD的SWD Adapter,OpenOCD的cfg檔案內容如下:</div><div style="text-align: left;">Pi 2b:(已測試)</div><div style="text-align: left;"># /usr/local/share/openocd/scripts/interface/rpi2.cfg</div><div style="text-align: left;"><div># Use RPI GPIO pins</div><div>adapter driver bcm2835gpio</div><div>bcm2835gpio_peripheral_base 0x3F000000</div><div><br /></div><div>bcm2835gpio_speed_coeffs 146203 36</div><div><br /></div><div># SWD swclk swdio</div><div># Header pin numbers: 23 22</div><div>bcm2835gpio_swd_nums 11 25</div><div><br /></div><div>transport select swd</div><div><br /></div><div>adapter speed 1000</div><div><br /></div></div><div style="text-align: left;">Pi Zero (Pi 1):</div><div style="text-align: left;"><div># /usr/local/share/openocd/scripts/interface/rpizero.cfg</div><div></div><div># Use RPI GPIO pins</div><div>adapter driver bcm2835gpio</div><div>bcm2835gpio_peripheral_base 0x20000000</div><div><br /></div><div>bcm2835gpio_speed_coeffs 113714 28</div><div><br /></div><div># SWD swclk swdio</div><div># Header pin numbers: 23 22</div><div>bcm2835gpio_swd_nums 11 25</div><div><br /></div><div>transport select swd</div><div><br /></div><div>adapter speed 1000</div><div><br /></div></div><div style="text-align: left;"><br /></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-39338286329381631742021-03-27T21:57:00.002+08:002021-03-27T21:58:08.735+08:00RaspberryPi Pico C++ IDE開發實做 (QtCreator)<p>前言:</p><div style="text-align: left;">RaspberryPi出Pico版本,開發比較接近Arduino,是non-OS的系統開發。</div><div style="text-align: left;">Pico提供C/C++和MicroPython開發方式。</div><div style="text-align: left;">因為我比較習慣用QtCreator開發C/C++程式,因此嘗試用QtCreator當IDE。</div><p>系統環境:</p><p>Ubuntu 18.04</p><p>套件安裝:</p><div style="text-align: left;">1. cmake安裝</div><div style="text-align: left;">先參考<a href="https://apt.kitware.com/" rel="nofollow" target="_blank">https://apt.kitware.com/</a>透過下面步驟安裝最新版cmake。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">1a. 新增kitware相關的key</div><div style="text-align: left;"># wget -O - <a href="https://apt.kitware.com/keys/kitware-archive-latest.asc">https://apt.kitware.com/keys/kitware-archive-latest.asc</a> 2>/dev/null | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null</div><div style="text-align: left;"><br /></div><div style="text-align: left;">1b. 新增kitware ppa</div><div style="text-align: left;"># apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main'</div><div style="text-align: left;"># apt-get update</div><div style="text-align: left;"><br /></div><div style="text-align: left;">1c. 安裝cmake</div><div style="text-align: left;"># apt install cmake</div><div style="text-align: left;"><br /></div><div style="text-align: left;">2. 安裝QrCreator和Pico需要的Cross-Compiler</div><div style="text-align: left;"># apt install git qtcreator <span style="color: #24292e; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px;">gcc-arm-none-eabi libnewlib-arm-none-eabi</span></div><div style="text-align: left;"><span style="color: #24292e; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px;"><br /></span></div><p>3. QtCreator新增專案</p><div style="text-align: left;">3a. 開啟QtCreator</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-B485hW4CWss/YF8wuRDix2I/AAAAAAAAwiE/1SU3K0DWrqce9JPhckIJrQjEUGExMhzSACLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="501" data-original-width="759" height="422" src="https://lh3.googleusercontent.com/-B485hW4CWss/YF8wuRDix2I/AAAAAAAAwiE/1SU3K0DWrqce9JPhckIJrQjEUGExMhzSACLcBGAsYHQ/w640-h422/image.png" width="640" /></a></div><br /></div><div style="text-align: left;">3b. 建立Non-Qt C++ Application專案</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-jG1S61JSWh4/YF8w_eo1NSI/AAAAAAAAwiQ/HAKYAjrisSgw7AAGnFWJ6SvkPQtkoe3_gCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="551" data-original-width="888" height="398" src="https://lh3.googleusercontent.com/-jG1S61JSWh4/YF8w_eo1NSI/AAAAAAAAwiQ/HAKYAjrisSgw7AAGnFWJ6SvkPQtkoe3_gCLcBGAsYHQ/w657-h398/image.png" width="657" /></a></div><br /><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;">3c. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-59O0UAKBl4A/YF8xaJrowyI/AAAAAAAAwiY/vwbAk4ybOJkwERNf5iL0XcGpQqsyBaQCQCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="531" data-original-width="808" height="420" src="https://lh3.googleusercontent.com/-59O0UAKBl4A/YF8xaJrowyI/AAAAAAAAwiY/vwbAk4ybOJkwERNf5iL0XcGpQqsyBaQCQCLcBGAsYHQ/w657-h420/image.png" width="657" /></a></div><div style="text-align: left;"><br /></div>3d. 選擇CMake<br /><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-kXOZ-jqia-g/YF8x1pFAQ_I/AAAAAAAAwig/Xuz6iGJ_Lf4NO__FE0i97-h_ToGoFp6dQCLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="500" data-original-width="800" height="400" src="https://lh3.googleusercontent.com/-kXOZ-jqia-g/YF8x1pFAQ_I/AAAAAAAAwig/Xuz6iGJ_Lf4NO__FE0i97-h_ToGoFp6dQCLcBGAsYHQ/w640-h400/image.png" width="640" /></a></div><div style="text-align: left;"><br /></div>3e. 選擇預設Kit<br /><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-cueBJ9LYj3U/YF8yO6rcqBI/AAAAAAAAwio/MtOJpyDKoNcD-nQhymeHrePbbIXH5cW0ACLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="531" data-original-width="808" height="420" src="https://lh3.googleusercontent.com/-cueBJ9LYj3U/YF8yO6rcqBI/AAAAAAAAwio/MtOJpyDKoNcD-nQhymeHrePbbIXH5cW0ACLcBGAsYHQ/w640-h420/image.png" width="640" /></a></div><br />3f. 目前不使用版本控制</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-t6qY3fJEBhs/YF8yamo8ofI/AAAAAAAAwis/dvKT_NI1alEA7bKRb628PR7rjbxJLt-LQCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="531" data-original-width="808" height="420" src="https://lh3.googleusercontent.com/-t6qY3fJEBhs/YF8yamo8ofI/AAAAAAAAwis/dvKT_NI1alEA7bKRb628PR7rjbxJLt-LQCLcBGAsYHQ/w656-h420/image.png" width="656" /></a></div><br /><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><p><br /></p><p>4. 在目錄內git下載pico-sdk</p><div style="text-align: left;">$ cd /tmp/my_pico_cpp_test</div><div style="text-align: left;">$ git clone https://github.com/raspberrypi/pico-sdk</div><div style="text-align: left;">$ cd pico-sdk</div><div style="text-align: left;">$ git submodule update --init</div><p>5. 複製<span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">pico_sdk_import.cmake (參考<a href="https://github.com/raspberrypi/pico-sdk" rel="nofollow" target="_blank">pico-sdk github</a>)</span></p><div>$ cd /tmp/my_pico_cpp_test</div><div>$ cp <span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">pico-sdk/external/pico_sdk_import.cmake ./</span></div><p>6. 修改CMakeLists.txt<span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"> (參考</span><a href="https://github.com/raspberrypi/pico-sdk" rel="nofollow" style="font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" target="_blank">pico-sdk github</a><span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">)</span></p><p><span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"></span></p><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-rCDnnwkX8VI/YF80WJqpU2I/AAAAAAAAwi4/Y_u7VxollrcXbUFMQeY07GFS3g7lxiM7gCLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="459" data-original-width="658" height="446" src="https://lh3.googleusercontent.com/-rCDnnwkX8VI/YF80WJqpU2I/AAAAAAAAwi4/Y_u7VxollrcXbUFMQeY07GFS3g7lxiM7gCLcBGAsYHQ/w640-h446/image.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">PS1: 專案內所有cpp檔案都加在 add_executable() 內</div><div class="separator" style="clear: both; text-align: left;">PS2: 主程式和產出的uf2名稱加在 add_executable()、target_link_libraries()、pico_add_extra_outputs() 三處內</div><br />7. QtCreator新增C++ Class<p></p><div style="text-align: left;"><span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><div class="separator" style="clear: both; text-align: left;">7a. 檔案 -> 新增檔案或專案 -> C++ -> C++ Class</div><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-MUOWYckambw/YF81X8fjx2I/AAAAAAAAwjA/rHx2-46igWIHR1PuTHFZyNJ-ZctRWN30wCLcBGAsYHQ/image.png" style="clear: left; float: left; letter-spacing: 0.1px; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="551" data-original-width="888" height="398" src="https://lh3.googleusercontent.com/-MUOWYckambw/YF81X8fjx2I/AAAAAAAAwjA/rHx2-46igWIHR1PuTHFZyNJ-ZctRWN30wCLcBGAsYHQ/w673-h398/image.png" width="673" /></a><a href="https://lh3.googleusercontent.com/-MUOWYckambw/YF81X8fjx2I/AAAAAAAAwjA/rHx2-46igWIHR1PuTHFZyNJ-ZctRWN30wCLcBGAsYHQ/image.png" style="clear: left; float: left; letter-spacing: 0.1px; margin-bottom: 1em; margin-right: 1em;"><br /></a></div><div style="text-align: left;"><span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div>7b. 設定LedWriter Class</span></div><div style="text-align: left;"><span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-OT50HhPlS1g/YF81-LBRlLI/AAAAAAAAwjI/n6bGgo_PtT40_I-qJtcF2b6iWy_Ma9E5wCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="543" data-original-width="808" height="430" src="https://lh3.googleusercontent.com/-OT50HhPlS1g/YF81-LBRlLI/AAAAAAAAwjI/n6bGgo_PtT40_I-qJtcF2b6iWy_Ma9E5wCLcBGAsYHQ/w663-h430/image.png" width="663" /></a></div><br /><br /></span></div><p><span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></p><p><span style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></p><p><br /></p><div style="text-align: left;"><br /></div><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p>8. 參考blink.c實做LED GPIO Output</p><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-WoGTjcVHblE/YF83OiOlRMI/AAAAAAAAwjQ/jJ5NiJXKTwYGhnRrH1tiP6xWW0NjgFIqQCLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="653" data-original-width="1054" height="396" src="https://lh3.googleusercontent.com/-WoGTjcVHblE/YF83OiOlRMI/AAAAAAAAwjQ/jJ5NiJXKTwYGhnRrH1tiP6xWW0NjgFIqQCLcBGAsYHQ/w659-h396/image.png" width="659" /></a></div><br /><br /></div><div style="text-align: left;">9. 實做main.cpp</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-_LhllhnxjKQ/YF83lYq1u1I/AAAAAAAAwjY/XQVQJZAsgtINatHRt6E2DXOgfU-3l1eQgCLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="277" data-original-width="502" height="354" src="https://lh3.googleusercontent.com/-_LhllhnxjKQ/YF83lYq1u1I/AAAAAAAAwjY/XQVQJZAsgtINatHRt6E2DXOgfU-3l1eQgCLcBGAsYHQ/w640-h354/image.png" width="640" /></a></div><br /></div><p>10. 建置專案</p><div style="text-align: left;">執行</div><div style="text-align: left;">Rescan Project</div><div style="text-align: left;">Clear CMake Configure</div><div style="text-align: left;">全部清理</div><div style="text-align: left;">清理專案</div><div style="text-align: left;">全部建置</div><p>11. 產出檔案</p><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-n9_VcOUxp-k/YF84pRJbVfI/AAAAAAAAwjg/H2rIIbmFPfkQxH8lmXt6hIZd6K781SWGQCLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="306" data-original-width="710" height="276" src="https://lh3.googleusercontent.com/-n9_VcOUxp-k/YF84pRJbVfI/AAAAAAAAwjg/H2rIIbmFPfkQxH8lmXt6hIZd6K781SWGQCLcBGAsYHQ/w651-h276/image.png" width="651" /></a></div></div><div style="text-align: left;"><br /></div><p>12. 燒錄檔案到RaspberryPi Pico</p><div style="text-align: left;">參考Pico官方文件</div><div style="text-align: left;">12a. Pico USB拔除</div><div style="text-align: left;">12b. 按著白色按鈕</div><div style="text-align: left;">12c. 插入Pico USB到電腦</div><div style="text-align: left;">12d. 跳出RPI-RP2 USB資料夾</div><div style="text-align: left;">12e. 把piblink.uf2 檔案複製到上述資料夾內</div><div style="text-align: left;">12f. Pico會自動Reset,並執行piblink.uf2</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><a href="https://lh3.googleusercontent.com/-yCund_DOqWM/YF85kmQR4MI/AAAAAAAAwjo/8_-QFJN3gV0ZK3BTtyZ21_msP5AlYSQ-QCLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="511" data-original-width="565" height="580" src="https://lh3.googleusercontent.com/-yCund_DOqWM/YF85kmQR4MI/AAAAAAAAwjo/8_-QFJN3gV0ZK3BTtyZ21_msP5AlYSQ-QCLcBGAsYHQ/w640-h580/image.png" width="640" /></a></div><br /><br /></div><p><br /></p>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-35779086499252285032021-03-23T18:22:00.008+08:002021-03-23T18:38:07.475+08:00Linux Chroot實作 (Debian)<p><b><span style="color: #2b00fe;">前言:</span></b></p><div style="text-align: left;">Linux Container(容器化)現在已經很常見了,但討論主要集中在Docker,Docker有非常多優勢,像是Docker Hub、Docker Volume、Docker Image與Container,進一步擴展還有K8s。<br />但Docker使用容量有點大,不論是Docker Image、Container,Docker相對有點點複雜,對於服務隔離而且硬體受限(低階VPS)的場景,找到Chroot,也就是Container的始祖。<br />相對於Container,Chroot比較少人討論,因為它僅有執行環境隔離,沒有CPU、記憶體隔離,也沒有網路隔離,但以服務隔離的角度,網路由防火牆防護,似乎不錯,於是實做了下,寫一篇分享。</div><p><b><span style="color: #2b00fe;">環境:</span></b></p><div style="text-align: left;">相同作法應該適用在Ubuntu/Debian系列的實體機和VM內,不適用Container內(LXC、Docker)。</div><div style="text-align: left;">實驗環境是<b>Debian 10 (Buster)</b>。</div><p><b><span style="color: #2b00fe;">安裝:</span></b></p><div style="text-align: left;"><b>1. 需要安裝2個套件</b>,包括debootstrap和schroot。</div><div style="text-align: left;"><i># apt-get install debootstrap schroot</i></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>2. 建立Chroot系統目錄</b></div><div style="text-align: left;"><i># <span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">mkdir /home/chroot</span></i></div><div style="text-align: left;"><br /></div><div style="text-align: left;">(這裡有個選項,如果有額外的磁碟或分割區,目錄可以用ZFS,好處是ZFS有snapshot功能,能讓Chroot系統支援snapshot)</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>3. 產生Chroot系統的Template</b></div><div style="text-align: left;"><i># debootstrap buster /home/chroot/rootfs http://ftp.tw.debian.org/debian/</i></div><div style="text-align: left;">(debootstrap能產生Debian/Ubuntu系統的Rootfs,裡面會包含Standard版本的完整系統,不包含fstab之類的設定)</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>4. 把Template打包</b></div><div style="text-align: left;"><i># cd /home/chroot</i></div><div style="text-align: left;"><i># tar cjf chroot.tar.bz2 rootfs</i></div><p><b><span style="color: #2b00fe;">設置Chroot系統:</span></b></p><div style="text-align: left;"><b>1. 解壓縮並更名Template</b></div><div style="text-align: left;"><i># cd /home/chroot</i></div><div style="text-align: left;"><i># tar jxf chroot.tar.bz2</i></div><div style="text-align: left;"><i># mv rootfs webrootfs</i> (預計要使用的Chroot系統名稱,這裡用webrootfs)</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>2. 編輯schroot的Chroot系統設定檔</b></div><div style="text-align: left;"><i># vi /etc/schroot/chroot.d/webrootfs.conf</i></div><div style="text-align: left;"><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">[webrootfs]</span><br style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" /><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">description=Web Rootfs</span><br style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" /><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">directory=/home/chroot/webrootfs</span><br style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" /><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">root-users=root</span><br style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" /><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">users=myuser</span><br style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" /><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">type=directory</span></div><p><b><span style="color: #2b00fe;">進入Chroot系統:</span></b></p><div style="text-align: left;">執行指令切換到Chroot系統</div><div style="text-align: left;"><i># <span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">schroot -c webrootfs</span></i></div><div style="text-align: left;"><br /></div><div style="text-align: left;">切換到Chroot系統後,所有的操作都在Chroot系統內,這時候可以安裝套件,準備執行環境。</div><p><b><span style="color: #2b00fe;">操作Chroot系統與執行程式:</span></b></p><div style="text-align: left;"><b>1. 啟動Chroot系統Session</b></div><div style="text-align: left;"><i># schroot -c webrootfs -b</i></div><div style="text-align: left;">執行後會出現一串像是:</div><div style="text-align: left;">webrootfs-b7738702-b79a-4376-9952-377570356804</div><div style="text-align: left;"><br /></div><div style="text-align: left;">這是這個Session的SessionID</div><div style="text-align: left;">(-c 指定Chroot系統名稱)</div><div style="text-align: left;">(-b 啟動Chroot系統,產生Session)</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>2. 查詢所有SessionID</b></div><div style="text-align: left;"><i># ls /run/schroot/mount/</i></div><div style="text-align: left;">所有SessionID都會紀錄在 /run/schroot/mount/</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>3. 進入Session</b></div><div style="text-align: left;"><i># schroot -r -c webrootfs-b7738702-b79a-4376-9952-377570356804</i></div><div style="text-align: left;">當SHELL出現(webrootfs)root 就是進入Session了</div><div style="text-align: left;"><div>(-c 指定SessionID)</div><div>(-r 進入存在的Session)</div><div><br /></div></div><div style="text-align: left;"><b>4. 離開Session</b></div><div style="text-align: left;"><i># exit</i></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>5. 刪除Session</b></div><div style="text-align: left;"><i># schroot -e -c webrootfs-b7738702-b79a-4376-9952-377570356804</i></div><div style="text-align: left;"><div>(-c 指定SessionID)</div><div>(-e 刪除存在的Session)</div></div><div style="text-align: left;"><br /></div><p><b><span style="color: #2b00fe;">參考:</span></b></p><div style="text-align: left;"><span face="Roboto, Arial, sans-serif" style="color: #1155cc;"><a href="http://logan.tw/posts/2018/02/24/manage-chroot-environments-with-schroot/" rel="nofollow" target="_blank">http://logan.tw/posts/2018/02/24/manage-chroot-environments-with-schroot/</a><br /></span></div><div style="text-align: left;"><a href="https://wiki.debian.org/Schroot" rel="nofollow" target="_blank">https://wiki.debian.org/Schroot</a></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-9750120635687819202021-02-18T13:38:00.003+08:002021-02-18T13:40:10.754+08:00Python Qt (PySide) 起手勢<div style="text-align: left;">在Python討論區看到有人用PySide實作視窗程式,因此嘗試用PySide寫出Python版本的Qt樣板程式。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">用過QtCreator的會發現,產生QWidget (或QDialog) 的Project後,會產生一個預設樣式的程式碼框架,但在PySide上,比較少看到,尤其加上Qt Designer之後,pyside-uic會產生UI Python程式碼,如何和其他程式碼整合,是個入門的門檻,因此寫一個PySide版的Template和作法。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">PySide Template程式碼分3塊:</div><div style="text-align: left;">1. Main主程式</div><div style="text-align: left;">2. Dialog主體</div><div style="text-align: left;">3. Qt Designer產生的Dialog內容</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Main主程式:</div><div style="text-align: left;"><div style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="font-weight: bold;">import</span> sys</div><div><span style="font-weight: bold;">from</span> PySide.QtGui <span style="font-weight: bold;">import</span> *</div><div><span style="font-weight: bold;">from</span> PySide.QtCore <span style="font-weight: bold;">import</span> *</div><br /><div><span style="font-weight: bold;">from</span> main_widget <span style="font-weight: bold;">import</span> MainWidget</div><br /><div><span style="font-weight: bold;">if</span> __name__ == <span style="color: #dd1144;">'__main__'</span>:</div><div> app = QApplication(sys.argv)</div><div> </div><div> main_widget = MainWidget()</div><div> main_widget.show()</div><br /><div> sys.exit(app.exec_())</div><br /></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Dialog主體:</div><div style="text-align: left;"><div style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="font-weight: bold;">from</span> PySide.QtGui <span style="font-weight: bold;">import</span> *</div><div><span style="font-weight: bold;">from</span> PySide.QtCore <span style="font-weight: bold;">import</span> *</div><br /><div><span style="font-weight: bold;">from</span> ui_text_dialog <span style="font-weight: bold;">import</span> Ui_Dialog</div><br /><div><span style="font-weight: bold;">class</span> MainWidget(QDialog):</div><br /><div> ui = Ui_Dialog()</div><br /><div> <span style="font-weight: bold;">def</span> __init__(<span style="font-weight: bold;">self</span>):</div><div> QDialog.__init__(<span style="font-weight: bold;">self</span>)</div><div> <span style="font-weight: bold;">self</span>.ui.setupUi(<span style="font-weight: bold;">self</span>)</div><div> <span style="font-weight: bold;">self</span>.ui.CloseButton.setText(<span style="color: #dd1144;">"關閉視窗"</span>)</div><br /><div> <span style="font-weight: bold;">self</span>.ui.CloseButton.clicked.connect(<span style="font-weight: bold;">self</span>.on_CloseButton_clicked)</div><br /><div> <span style="font-weight: bold;">def</span> on_CloseButton_clicked(<span style="font-weight: bold;">self</span>):</div><div> QApplication.exit(<span style="color: #009999;">0</span>)</div></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Qt Designer產生的Dialog內容:</div><div style="text-align: left;">Qt設計師產生後,儲存的檔名以ui_開頭做為區別。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">框架原理如下:</div><div style="text-align: left;">Main主程式內等同於Qt的main.cpp,主要放的是QApplication和包含Config在內其他初始化程式。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Dialog程式內放的是實際視窗的程式,等同於Qt的widget.cpp(dialog.cpp)和widget.h(dialog.h),實際的視窗程式所在,包括視窗功能,Signal/Slot定義、連接、動作都寫在這裡。</div><div style="text-align: left;">Dialog程式內的關鍵有3個:</div><div style="text-align: left;">1. 繼承QDialog</div><div style="text-align: left;"> <span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: bold; white-space: pre;">class</span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;"> MainWidget(QDialog)</span></div><div style="text-align: left;"><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;"><br /></span></div><div style="text-align: left;">2. 在 <span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">__init__(</span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: bold; white-space: pre;">self</span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">)</span> 建構子內,要呼叫QDialog的建構子</div><div style="text-align: left;"> <span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">QDialog.__init__(</span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: bold; white-space: pre;">self</span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">)</span></div><div style="text-align: left;"><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;"><br /></span></div><div style="text-align: left;"><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">3. 透過 </span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">setupUi() 把 Qt Designer 的 ui_dialog 整個載入,並把dialog指定到 </span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">Qt Designer 的 ui_dialog</span></div><div style="text-align: left;"><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;"> </span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: bold; white-space: pre;">self</span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">.ui.setupUi(</span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: bold; white-space: pre;">self</span><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;">)</span></div><div style="text-align: left;"><span style="background-color: white; color: #2e2e2e; font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback"; font-size: 14px; white-space: pre;"><br /></span></div><div style="text-align: left;">Qt Designer產生的ui_dialog,直接由Qt Designer產生就能套用,objectName要設定,Signal/Slot不寫在這,直接寫在Dialog的實體中。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">程式碼:</div><div style="text-align: left;"><a href="https://gitlab.com/ycfunet/pyside_template_test">https://gitlab.com/ycfunet/pyside_template_test</a></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-23345511876865873432020-12-07T16:42:00.004+08:002020-12-07T16:42:50.267+08:00Python版本的Event-Handler之前寫Qt時,connect-slot用得很開心,看c#才知道原來這個設計稱之為Event-Handler,<div>基本上,UI Framework很多都用Event-Handler設計的。</div><div><br /></div><div>在Python上,因為先前程式複雜度還沒這麼高,也沒有UI需求,因此一直沒有玩Event-Handler,</div><div>最近心血來潮找了下,把用法和sample code貼出來。</div><div><a href="https://gitlab.com/ycfunet/event_handler_test.git" target="_blank">https://gitlab.com/ycfunet/event_handler_test.git</a><br /></div><div><br /></div><div><pre style="background-color: white; color: #080808; font-family: 'JetBrains Mono',monospace; font-size: 9.8pt;">Python<span style="font-family: 'DejaVu Sans Mono',monospace;">版本的</span>Event-Handler (connect-slots)<span style="font-family: 'DejaVu Sans Mono',monospace;">,要先安裝套件</span>events<span style="font-family: 'DejaVu Sans Mono',monospace;">。<br /></span><span style="font-family: 'DejaVu Sans Mono',monospace;">使用步驟:<br /></span><span style="color: #1750eb;">1. </span><span style="font-family: 'DejaVu Sans Mono',monospace;">設定</span>Signal function Name<br />self.events = Events((<span style="color: teal; font-weight: bold;">'on_change'</span>, <span style="color: teal; font-weight: bold;">'on_trigger'</span>))<br />on_change, on_trigger<span style="font-family: 'DejaVu Sans Mono',monospace;">都是</span>Signal function Name<br /><br /><span style="color: #1750eb;">2. </span><span style="font-family: 'DejaVu Sans Mono',monospace;">發出</span>Signal<span style="font-family: 'DejaVu Sans Mono',monospace;">的來源端要把</span>Events<span style="font-family: 'DejaVu Sans Mono',monospace;">指定進去</span>(Slot<span style="font-family: 'DejaVu Sans Mono',monospace;">端不用</span>)<br />self.button = Button(self.events)<br />self.display = Display()<br /><br /><span style="color: #1750eb;">3. </span><span style="font-family: 'DejaVu Sans Mono',monospace;">實做</span>Slot function<br /><span style="color: #0033b3;">class </span>Display:<br />.....<br /> <span style="color: #0033b3;">def </span>show_button(self, num):<br /> self.NUMBER = num<br />.....<br /><br /><span style="color: #1750eb;">4. </span><span style="font-family: 'DejaVu Sans Mono',monospace;">將</span>Slot function<span style="font-family: 'DejaVu Sans Mono',monospace;">和</span>Signal function<span style="font-family: 'DejaVu Sans Mono',monospace;">連結<br /></span>self.events.on_change += self.display.show_button<br /><br /><span style="color: #1750eb;">5. </span><span style="font-family: 'DejaVu Sans Mono',monospace;">觸發</span>Signal function<br /><span style="color: #0033b3;">class </span><span style="color: black;">Button</span>:<br />.....<br /> EVENTS = <span style="color: #0033b3;">None<br /></span>.....<br /> <span style="color: #0033b3;">def </span><span style="color: #b200b2;">__init__</span>(self, events):<br /> self.EVENTS = events<br />.....<br /> <span style="color: #0033b3;">def </span><span style="color: black;">run</span>(self):<br />.....<br /> self.EVENTS.on_change(<span style="color: #1750eb;">6</span>)<br />.....<br /><span style="font-family: 'DejaVu Sans Mono',monospace;">看得出來沒有</span>Thread-Safe<span style="font-family: 'DejaVu Sans Mono',monospace;">,因此如果要用在多執行緒內,要另外加上</span>mutex<span style="font-family: 'DejaVu Sans Mono',monospace;">。<br /></span></pre><pre style="background-color: white; color: #080808; font-family: 'JetBrains Mono',monospace; font-size: 9.8pt;"><span style="font-family: 'DejaVu Sans Mono',monospace;"><br /></span></pre></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-40729200014341783972020-11-22T15:08:00.003+08:002020-11-22T15:21:04.694+08:00Ai學習 - 1_PVE LXC Nvidia GPU Tensorflow環境搭設<div>最近想學Ai,早在去年從同事買了一張挖礦汰換的顯示卡,</div><div>最近才把它裝機,準備開始用。</div><div>起手式第一步,環境搭設。</div><div><br /></div><div>Tensorflow官方有提供Docker,我也的確裝上去了,</div><div>但我發現Pycharm對於遠端Docker和遠端Virtualenv的支援不太好,</div><div>遠端SSH支援不錯,但Tensorflow Docker在遠端SSH除錯會出錯誤,</div><div>嘗試自己建標準版Ubuntu 18.04 Docker,遠端SSH除錯和執行都確定沒問題,</div><div>目前把問題歸在官方Docker,於是就土法煉鋼自己做了。</div><div>下面就把過程和步驟整理出來。</div><div><br /></div><div>Tensorflow GPU需要5樣東西(依階層):</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-cz4AuKEdNkI/X7n_MNBy89I/AAAAAAAAvPA/fYEr1f1eikAM5fxaR867IME5WemaeEdCQCLcBGAsYHQ/s356/Tensorflow%25E9%259A%258E%25E5%25B1%25A4%25E5%259C%2596.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: left;"><img border="0" data-original-height="356" data-original-width="267" height="320" src="https://1.bp.blogspot.com/-cz4AuKEdNkI/X7n_MNBy89I/AAAAAAAAvPA/fYEr1f1eikAM5fxaR867IME5WemaeEdCQCLcBGAsYHQ/s320/Tensorflow%25E9%259A%258E%25E5%25B1%25A4%25E5%259C%2596.png" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>不論是Docker還是LXC,在Host主機上,都只需要安裝<span style="color: #2b00fe;"><b>NVIDIA顯示卡驅動程式</b></span>,</div><div>其他部份(<span style="color: #2b00fe;"><b>NVIDIA CUDA、NVIDIA cuDNN、Python、Tensorflow-GPU</b></span>)都在Guest的Docker或LXC內,如下圖:</div><div class="separator" style="clear: both; text-align: left;"><a href="https://1.bp.blogspot.com/-gay9AP5lafw/X7oBCC7uxsI/AAAAAAAAvPM/ZZK9lhPO3gI573lu4wHTRnVxj3zdC9jEACLcBGAsYHQ/s636/Tensorflow%25E9%259A%258E%25E5%25B1%25A4%25E5%259C%2596_LXC_Docker.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="344" data-original-width="636" src="https://1.bp.blogspot.com/-gay9AP5lafw/X7oBCC7uxsI/AAAAAAAAvPM/ZZK9lhPO3gI573lu4wHTRnVxj3zdC9jEACLcBGAsYHQ/s16000/Tensorflow%25E9%259A%258E%25E5%25B1%25A4%25E5%259C%2596_LXC_Docker.png" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>而在Container內要跟Host之間聯結,讓Container的程式能存取NVIDIA顯示卡驅動程式,</div><div>因此需要一些額外設定。</div><div>在Docker中,需要安裝NVIDIA修改過的Docker (nvidia-docker),</div><div>並在執行docker時指定runtime=nvidia,指令大致是:</div><div># <span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">docker run --dns 8.8.8.8 -p 8888:8888 -v tensorflow-data:/tf/data --runtime=nvidia -d --rm tensorflow/tensorflow:latest-gpu-py3-jupyter</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">細節就不多說了,下面操作步驟在描述。</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">操作步驟:</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">1. 安裝NVIDIA驅動程式</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">在PVE上安裝NVIDIA顯示卡驅動程式,步驟主要3點:</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">1a. NVIDIA官方網站下載相對應的Linux amd64驅動程式</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">1b. 新增/修改 </span><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">/etc/modprobe.d/blacklist.conf 如下:</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">/etc/modprobe.d/blacklist.conf</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">================</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">b</span><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">lacklist nouveau</span></div><div><div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">================</span></div></div></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">1c. 執行Linux amd64驅動程式進行安裝</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># bash /root/NVIDIA-Linux-x86_64-450.80.02.run</span></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">1d. 確認安裝完成</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># nvidia-smi</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">如果安裝成功,nvidia-smi會顯示系統顯示卡的資訊。</span></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span style="color: #202124;">這邊要記住安裝的驅動程式版本,這裡用</span><b><span style="color: #2b00fe;">450.80.02</span></b><span style="color: #202124;">。</span></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">2. 建立Ubuntu 18.04 LXC Container</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">1a. 在PVE上建立Ubuntu 18.04 LXC,這裡用標準的standard。</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">1b. 接著要修改LXC的設定檔,</span></span><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">將NVIDIA顯示卡驅動程式產生的device file映射到Container中,</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">在PVE LXC的設定檔加入:</span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">/etc/pve/lxc/105.conf</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">================</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">..........</span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">lxc.cgroup.devices.allow: c 195:* rwm
lxc.cgroup.devices.allow: c 243:* rwm
lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file</span></span><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">
</span></div><div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><div style="color: black; font-size: medium; font-variant-ligatures: normal; letter-spacing: normal; white-space: normal;"><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">================</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">細節參考:</span></div></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><a href="https://medium.com/@MARatsimbazafy/journey-to-deep-learning-nvidia-gpu-passthrough-to-lxc-container-97d0bc474957">https://medium.com/@MARatsimbazafy/journey-to-deep-learning-nvidia-gpu-passthrough-to-lxc-container-97d0bc474957</a></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><br /></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">3. 下載NVIDIA CUDA (Container內)</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">NVIDIA網站下載CUDA:</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><a href="https://developer.nvidia.com/cuda-10.0-download-archive">CUDA Toolkit 10.0 (版本沒差,在apt install時都能選)</a></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><br /></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">選擇 Linux -> x86_64 -> Ubuntu -> 18.04 -> Deb(Network)</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><br /></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">接著按照網站上描述的步驟,執行</span><span face="Roboto, Arial, sans-serif" style="color: #2b00fe;"><b>前面3點</b></span><span face="Roboto, Arial, sans-serif" style="color: #202124;">。</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"># </span></span><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">dpkg -i cuda-repo-ubuntu1804_10.0.130-1_amd64.deb</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># apt update</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">4. 安裝NVIDIA顯示卡工具程式、CUDA (Container內)</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">我們上個步驟把套件庫新增到系統後,這邊安裝。</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">這裡安裝要注意幾點:</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">1. Tensorflow-GPU不同版本相依不同的CUDA版本</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">2. CUDA安裝會相依nvidia-driver、nvidia-util版本</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">因為Tensorflow會相對應特定版本的CUDA,版本參考:</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><a href="https://www.tensorflow.org/install/source">https://www.tensorflow.org/install/source</a></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">原則上,Tensorflow 2.1.0以上都用CUDA-10.1版,這裡安裝CUDA-10.1版。</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></span></div><div><span face="Roboto, Arial, sans-serif"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span style="color: #202124;">前面提到我們用的顯示卡驅動程式版本是</span><b><span style="color: #2b00fe;">450.80.02</span></b><span style="color: #202124;">,</span></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">這裡要安裝cuda-drivers-450.80.02,指令如下:</span></span></div><div><span face="Roboto, Arial, sans-serif"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span style="color: #202124;"># </span><span style="color: #2b00fe;"><b>apt-get install cuda-drivers=450.80.02-1</b></span></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">比較簡單的方式是,安裝aptitude,用aptitude指定這個版本安裝。</span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124;"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">接著才安裝CUDA,這裡我們安裝CUDA-10.1版,指令如下:</span></span></div><div><div><span face="Roboto, Arial, sans-serif"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span style="color: #202124;"># </span><span style="color: #2b00fe;"><b>apt-get install cuda-10-1</b></span></span></span></div></div><div><span face="Roboto, Arial, sans-serif"><span style="font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span style="color: #2b00fe;"><b><br /></b></span></span></span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">10-1表示10.1版,要安裝其他版本,例如10.0 <span style="letter-spacing: 0.1px;">(</span>就是cuda-10-0)。</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">同樣的,比較簡單的方式是用aptitude直接指定版本安裝。</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">這裡要注意,安裝時,它顯示把cuda-drivers移除,安裝最新的455版,</span></div><div><span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">一樣,用aptitude,直接指定cuda-drivers用450.80.02-1的版本。</span></div><div><br /></div><div>完成後,執行(Container內):</div><div># nvidia-smi</div><div><br /></div><div>應該會出現和PVE主機上一樣的畫面訊息。</div><div><br /></div><div>5. 安裝NVIDIA cnDNN</div><div>NVIDIA網站下載cnDNN</div><div><a href="https://developer.nvidia.com/rdp/cudnn-archive">https://developer.nvidia.com/rdp/cudnn-archive</a></div><div><br /></div><div>前面Tensorflow網站寫,2.1.0以上版本是7.6版,因此我下載:</div><div><b><span style="color: #2b00fe;">cuDNN v7.6.5, for CUDA 10.1</span></b></div><div><br /></div><div>選Ubuntu 18.04版本下載。</div><div>只需要安裝runtime即可,dev和doc都不用下載。</div><div>直接用</div><div># dpkg -i /root/libcudnn7_7.6.5.32-1+cuda10.0_amd64.deb</div><div><br /></div><div>6. 安裝Python3.7和pip3<span face="Roboto, Arial, sans-serif" style="color: #202124; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"> (Container內)</span></div><div>Tensorflow提供pip安裝,但要求要pip 19.0以上版本,</div><div>因此我們安裝並升級Ubuntu 18.04的Python3.7和pip3。</div><div><br /></div><div>6a. 安裝Python3.7</div><div># apt install python3.7 python3.7-dev</div><div><br /></div><div>6b. 調整預設Python3版本為python3.7</div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1</span><br style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" /><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 2</span><br style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" /><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># update-alternatives --config python3</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">6c. 安裝升級新版pip3</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># apt install python3-pip</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># wget </span><a href="https://bootstrap.pypa.io/get-pip.py" rel="nofollow" style="background-color: white; color: #6611cc; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; pointer-events: none; white-space: pre-wrap;" target="_blank">https://bootstrap.pypa.io/get-pip.py</a></div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"># python3.7 ./</span><a href="http://get-pip.py/" rel="nofollow" style="background-color: white; color: #6611cc; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; pointer-events: none; white-space: pre-wrap;" target="_blank">get-pip.py</a><br style="background-color: white; color: #202124; font-family: Roboto, Arial, sans-serif; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;" /><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><br /></span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">7. 安裝Tensorflow-GPU</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">這裡我看了半天,終於搞懂差異在哪,</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">首先Tensorflow 1.x 和 Tensorflow 2.x 不相容,程式碼要修改,</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">再來,Tensorflow 2.0.0、2.1.0、2.2.0、2.3.0穩定性有差異,</span></div><div><span face="Roboto, Arial, sans-serif" style="background-color: white; color: #202124; font-size: 16px; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;">支援的演算法有差異,演算法差異參考:</span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><a href="https://cloud.google.com/tpu/docs/supported-versions">https://cloud.google.com/tpu/docs/supported-versions</a></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><br /></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">目前我選2.2.1安裝,但之後Raspberry Pi + Tensorflow-Lite,我可能會改用2.3.1。</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><br /></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">安裝Tensorflow-2.2.1</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"># pip3 install tensorflow-gpu==2.2.1</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><br /></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">等等等等.......</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><br /></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">8. 驗證 & 備份</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">執行下面這段Python程式碼:</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">import tensorflow as tf</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">tf.test.gpu_test_gpu_device_name()</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;"><br /></span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">正常會顯示一堆東西,其中出現</span></span></div><div><span style="background-color: white; font-variant-ligatures: none; letter-spacing: 0.1px; white-space: pre-wrap;"><span face="Roboto, Arial, sans-serif" style="color: #202124;">GeForce GTX 1060 6GB computeCapability: 6.1</span></span></div><div><br /></div><div>接著最重要的步驟~備份。</div><div>Container關機 -> Backup -> 選 gzip -> 備份後,搬移到 templates/cache/ 改名為 tensorflow-gpu-2.2.1。</div><div><br /></div><div>到這裡,LXC版本的可遷徙容器完成.......</div><div>其他地方只需要PVE安裝個Nvidia Driver 450.80.02就可以玩Tensorflow了!</div><div><br /></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-29566875649821427372020-10-10T18:09:00.006+08:002020-10-10T18:16:11.140+08:00我認知的電力電子 - 6 總結前面多篇提到交換式電源、逆變器、變頻器,其實現在很多應用都是上述幾種變形,這邊總結的部份就把我知道的一些應用列出來。<div><br /></div><div>新式T6節能日光燈具:</div><div>T6節能日光燈具號稱能省電、燈具不閃爍,關鍵不是T6規格,而是燈具內使用是「預熱式電子安定器」,多年前上網買T5用的「預熱式電子安定器」,把家裡T5燈具全部拆下來更換安定器,效果的確不錯,每支燈管平均壽命大約3~4年,也的確不閃爍。</div><div>原理很簡單,就是交換式電源,傳統的安定器直接是交流電,頻率就是60Hz,而透過交換式電源設計,交換式電源的切換頻率大約50kHz,相對來說就是不閃爍,而交換式電源的電子安定器轉換效率能到93%左右。</div><div>不過現在家裡全部汰換了,都換成LED吸頂燈了,算是進化到3代燈具。</div><div><br /></div><div>變壓器:</div><div>手機用的變壓器,現在都很輕,都已經是交換式電源設計了。</div><div><br /></div><div>行動電源:</div><div>內部都是DC-DC Converter,小米有出2種,一種稱為高配版,一種是一般的版本,其實差別在,高配版內部是2組DC-DC Converter,所以功能上能夠同時行動電源充電和行動電源充手機。</div><div><br /></div><div>變頻冷氣、冰箱:</div><div>內部就是變頻器。</div><div><br /></div><div>新式電梯馬達控制:</div><div>新式電梯的馬達控制使用變頻控制,最新的稱為PM永磁式,是在變頻控制的基礎上,馬達內部變成多級,很多組的磁鐵和電磁鐵(傳統是三相,3組),稱為PM永磁式,好處是因為很多組,能提高扭力,搭配變頻控制,直接省去減速齒輪箱。</div><div><br /></div><div>電動車馬達控制:</div><div>電動車的馬達控制,就是變頻器,和前述差不多,有沒有永磁這類設計不確定。</div><div><br /></div><div>電動車煞車的電力回充:</div><div>電動車和油電車有個特色,具備煞車時的電力回充,效果有2個,一方便能夠減少煞車皮使用,另外能充點電。</div><div>馬達通電正著轉,是馬達,馬達反著轉,其實就變成發電機了。</div><div>煞車時的動能,讓馬達反轉,就變成發電。</div><div>但設計細節得查查看。</div><div><br /></div><div>變頻器與各種三相馬達控制:</div><div>就變頻器與馬達控制。</div><div><br /></div><div>UPS:</div><div>提過了。</div><div><br /></div><div>太陽能發電和並聯市電:</div><div>太陽能發電其實就是UPS設計的延伸,太陽能充電到電池,就是交換式電源設計,如果要進一步併入家中市電,就是多增加逆變器,把電池轉成交流電充入家中市電。</div><div>這裡逆變器的差別是,UPS是產生固定110v 60Hz的電力。</div><div>太陽能發電用的市電併聯器,裡面的頻率產生器是從市電參考目前的電壓和頻率,再把頻率複製後產生到開關電路控制,電壓和頻率是根據市電浮動的。</div><div><br /></div><div>UAV無人載具的馬達控制器:</div><div>前面提過遙控車的電變。</div><div><br /></div><div>差不多這些,基本上目前看得到的電機和馬達控制應用,只要是電力或電機類馬達和電力控制,大概都是差不多的東西。</div><div><br /></div><div>樹莓派的PWM,PWM就是頻率產生器,但因為電腦是數位的,原則上PWM提供的是方波1-0-1-0的週期方波信號,PWM的好處是能改變1-0-1-0的切換頻率。</div><div>但如果要轉交流的應用,直接買一塊頻率產生器板子,NT150,頻率穩定性效果遠勝PWM,控制又簡單。</div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-59745948197060595472020-10-10T17:40:00.006+08:002020-10-10T17:41:07.128+08:00我認知的電力電子 - 5 變頻器<div style="text-align: left;">前面提到交換式電源和逆變器。</div><div style="text-align: left;">這裡直白的描述,交換式電源因為是開關型電路,其實本質上就是AC-DC Converter。</div><div style="text-align: left;">因此常見的DC-DC Converter實際上是透過開關開啟和關閉的方式,把來源的DC轉成類似AC的切換模式,在轉成DC輸出。</div><div style="text-align: left;">而逆變器,就是DC-AC Converter。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">變頻器的目標,是透過改變頻率的方式,控制交流馬達。</div><div style="text-align: left;">這類的應用傳統的交流馬達應用,通常是AC(定頻60Hz)-AC馬達。</div><div style="text-align: left;">而要改換成變頻器,尤其是俗稱的「DC直流變頻」,其實就是:</div><div style="text-align: left;">AC -> AC-DC Converter -> DC-AC Converter -> AC交流馬達</div><div style="text-align: left;"><br /></div><div style="text-align: left;">然後在DC-AC Converter內控制AC的頻率,達成變頻的效果。</div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-54913536515403695092020-10-10T17:32:00.004+08:002020-10-10T17:32:43.528+08:00我認知的電力電子 - 4 UPS原理與逆變器前面提到交換式電源和遙控車的電變,提到電變的差異是,把開關電路的控制改為控制PowerMOS這類元件,達到大電流放電的能力。<div><br /></div><div>新式UPS的原理和電變差不多,因此理解交換式電源和電變之後,就比較容易理解UPS。</div><div><br /></div><div>UPS功能:</div><div>UPS主要是當停電時,提供裝置電力。</div><div>UPS內部有蓄電池,當停電發生時,會將電力供應從插座的交流電轉到蓄電池繼續供應電力,直到蓄電池沒電為止。</div><div>因此電路上包含蓄電池充電和蓄電池放電2個部份,這裡著重在蓄電池放電的描述。</div><div><br /></div><div>UPS蓄電池放電原理:</div><div>我們再看一次DC-DC Converter的示意圖:</div><div class="separator" style="clear: both; text-align: left;"><a href="https://1.bp.blogspot.com/-bAwRG40TdeA/X4FsYaHap5I/AAAAAAAAutE/NT-xFhub5NkTIJd3Z_gruvYIP1YvI4ldgCPcBGAYYCw/s931/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25962.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="249" data-original-width="931" height="172" src="https://1.bp.blogspot.com/-bAwRG40TdeA/X4FsYaHap5I/AAAAAAAAutE/NT-xFhub5NkTIJd3Z_gruvYIP1YvI4ldgCPcBGAYYCw/w640-h172/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25962.png" width="640" /></a></div><br /><div>在UPS中,蓄電池的負載放電也和DC-DC Converter相同,但2個問題點:</div><div>1. 負載很多或很大,需求電力大</div><div>2. 負載使用交流電</div><div><br /></div><div>因此,和電變一樣,UPS內的負載使用多顆PowerMOS這類電子元件提供電力,PowerMOS的數量可能多達8顆甚至更多,根據UPS設計的負載能力不同,使用的PowerMOS顆數不同。</div><div><br /></div><div>第2點,負載要是交流電,怎麼辦?</div><div>這是本篇的重點。</div><div><br /></div><div>頻率產生器:</div><div>現在有種東西,叫作頻率產生器,功能很簡單,根據參數設定,產生特定的頻率,頻率可以是正弦波、方波、甚至三角波,頻率也能設定。</div><div>它的單價很低,1個大約NT150(10年前價格)。</div><div><br /></div><div>前面交換式電源提到:</div><div>1. 整個DC-DC Converter會透過一個電壓偵測器偵測電壓,根據電壓控制充電的開關電路開啟和關閉的頻率。</div><div>2. 電容的一個特性是,電壓和電容量會在充電時持續上升。</div><div><br /></div><div>在UPS應用中,會加上一片頻率產生器,電壓偵測器會根據頻率產生器的正弦波,和電容的電壓比較,控制充電的開關電路開啟和關閉。</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-687YAI2sfBw/X4F9h49P_cI/AAAAAAAAutM/NaRKPmnq4xYn9eUbboWiAuVEQGfS0GUowCLcBGAsYHQ/s966/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="684" data-original-width="966" height="454" src="https://1.bp.blogspot.com/-687YAI2sfBw/X4F9h49P_cI/AAAAAAAAutM/NaRKPmnq4xYn9eUbboWiAuVEQGfS0GUowCLcBGAsYHQ/w640-h454/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="640" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>因為要提供的是交流電,因此充電部份會有2組,1組提供正電壓充電,1組提供反向的負電壓充電,負電壓這組,輸出時正負反接。</div><div>因為交流電是110v正弦波,因此電壓偵測器偵測電容電壓時,電壓要是110v正弦波的波型電壓。</div><div><br /></div><div>逆變器:</div><div>雖然整篇文章沒提到逆變器,但其實上面提到UPS的這種產生交流電的Converter,稱之為DC-AC Converter,俗稱Inverter,中文就是逆變器。</div><div><br /></div><div>這類的UPS比傳統UPS更好,一般號稱能夠推動馬達的UPS,內部多半是這種設計,而傳統的UPS設計,是透過多組不同電壓的直流方波疊加產生的,其實是很像金字塔的正弦方波,效果比較差。</div><div><br /></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-15352707845103015792020-10-10T16:36:00.001+08:002020-10-10T16:36:19.154+08:00我認知的電力電子 - 3 遙控車電變在上一篇的交換式電源中提到,交換式電源包含3個部份:<div>充電(開關電路)、電池(電容)、負載放電</div><div><br /></div><div>充電(開關電路)的目標是:</div><div>在負載持續放電(用電)下,透過開關的方式,持續對電容充電,並維持負載需要的電壓。</div><div><br /></div><div>有沒有可能,負載需要的電流太大,超過開關電路的供應?發生了會如何?</div><div>是很有可能發生的,當發生的時候,負載電路會持續以最大電流供電,電容會持續充放電,但負載用電太大,會讓電容電壓不足(充電無法到達需求電壓),充電電路會發熱,如果散熱不足,可能造成元件過熱燒毀。</div><div><br /></div><div>為了應對大電流電機應用,多年前開始出現PowerMOS這類電子元件,這類電子元件的功能就是電力開關,開啟時通電,關閉時不通電。</div><div>但這類電子元件的新的差異是,他開啟通電時,能持續提供15w甚至30w的電力供應,也就是12v提供1.5A甚至3A的電流,而且能夠並聯。</div><div>這類元件最典型的應用就是遙控車、無人載具馬達的供電電源。</div><div><br /></div><div>透過並聯,低一顆PowerMOS提供15w,2顆並聯供給1顆馬達,最大可以提供馬達30w電力,2顆馬達則整台遙控車最大出力可以達到60w。</div><div>這對於目標是大馬力高速度的遙控車,絕對是逸品。</div><div>而這樣的元件,在遙控車裏面,稱之為『電變』。</div><div><br /></div><div>而馬達的控制,就是前面開關電路的開啟跟關閉的頻率,調高開啟頻率,就讓馬達動力更強,降低開啟頻率,就讓馬達轉弱。</div><div><br /></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-6683458947353102782020-10-10T16:14:00.005+08:002020-10-10T16:14:56.583+08:00我認知的電力電子 - 2 交換式電源<div>交換式電源:</div>交換式電源大概是電力電子中基本的應用之一,也是最廣泛的應用。<div>交換式電源厲害的地方是,傳統變壓器重量重,轉換效率低,重量大概幾百克甚至幾公斤(根據變壓器大小),轉換效率大約60%左右,交換式電源重量只有幾克,轉換效率85%起跳,目前常見的行動電源和3.3v、5v變壓器,轉換效率都做到95%以上,甚至98%。</div><div><br /></div><div>原理:</div><div>整個交換式電源(Switch Power),我把它分成3個部份和階段:</div><div>●充電</div><div>●電池</div><div>●放電</div><div><br /></div><div><b>充電部份(階段)與電池部份(階段):</b></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-DnRHkHjwybs/X4FSnK2hlII/AAAAAAAAurY/YaNUgpSmhVk5_inmK28rYXbE9XaWpfGqACLcBGAsYHQ/s518/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="93" data-original-width="518" height="71" src="https://1.bp.blogspot.com/-DnRHkHjwybs/X4FSnK2hlII/AAAAAAAAurY/YaNUgpSmhVk5_inmK28rYXbE9XaWpfGqACLcBGAsYHQ/w400-h71/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="400" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>充電部份(階段)通常充電會是開關電路,電池會是電容,也因為充電是開關電路,因此稱為交換式電源(Switch Power)。</div><div>至於使用充電部份使用開關電路的原因,我猜可能跟交流電有關,實際原因我可能需要再查看看。</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-tF9mzT84vJI/X4FToMKIOsI/AAAAAAAAurg/by1Hk9WA6ZcT68e7X08fmVUnOIwG3locwCLcBGAsYHQ/s515/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="89" data-original-width="515" height="69" src="https://1.bp.blogspot.com/-tF9mzT84vJI/X4FToMKIOsI/AAAAAAAAurg/by1Hk9WA6ZcT68e7X08fmVUnOIwG3locwCLcBGAsYHQ/w400-h69/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="400" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>開關電路和電容的特性先看下圖:<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-6If61ubuSNY/X4Frf7buKwI/AAAAAAAAus0/2OFYCblUfZg4SYGsLS0F_SGpA2jTSYp8gCLcBGAsYHQ/s927/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="256" data-original-width="927" height="176" src="https://1.bp.blogspot.com/-6If61ubuSNY/X4Frf7buKwI/AAAAAAAAus0/2OFYCblUfZg4SYGsLS0F_SGpA2jTSYp8gCLcBGAsYHQ/w640-h176/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">上圖有個地方有問題,不過先不管它,解說為主。</div></div><div>以DC-DC Converter來說,假設是12v轉9v的DC-DC Converter,開關電路的特性是上圖左側。</div><div>當開關電路的開關開啟時,會產生12v電壓的電力。</div><div><br /></div><div>而電池或者電容的特性有幾個點:</div><div>1. 電容或電池的電壓</div><div>2. 電容或電池的容量(內部儲存的電力容量)</div><div>3. 電容的電壓會隨著電容量的多寡而變化</div><div><br /></div><div>因此,假設電容量一半時,則它的電壓可能只有一半,</div><div>當然,電容實際的電壓、電容量、電流要用算的,這邊先不用管這麼細。</div><div><br /></div><div>所以,電池或電容的特性如上圖右側,當電池或電容沒電時,電壓為0v,</div><div>隨著持續充電,電壓會隨電容量增加持續上升,直到到達我們需要的電壓。</div><div><br /></div><div>以12v轉9v的DC-DC Converter來說,當電容充電之後,電壓會從0v變成9v。</div><div><br /></div><div><b>放電部份(階段)與電池部份(階段):</b></div><div><div class="separator" style="clear: both; text-align: left;"><a href="https://1.bp.blogspot.com/-pUPGUgBz_wo/X4Fe11rMJmI/AAAAAAAAusM/eU8KdQ917oAaw7OeuuXEiEBmADzn8cmrACLcBGAsYHQ/s519/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="92" data-original-width="519" height="71" src="https://1.bp.blogspot.com/-pUPGUgBz_wo/X4Fe11rMJmI/AAAAAAAAusM/eU8KdQ917oAaw7OeuuXEiEBmADzn8cmrACLcBGAsYHQ/w400-h71/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="400" /></a></div><div><br /></div>同樣的,電池部份(階段)通常是電容,而放電部份(階段),通常是負載。</div><div class="separator" style="clear: both; text-align: left;"><a href="https://1.bp.blogspot.com/-8ZHw7Fxdwpg/X4FgHVVvLvI/AAAAAAAAusc/l41BfC-DsLwNSeUBEY24ND55yFqzPROfACLcBGAsYHQ/s513/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="89" data-original-width="513" height="70" src="https://1.bp.blogspot.com/-8ZHw7Fxdwpg/X4FgHVVvLvI/AAAAAAAAusc/l41BfC-DsLwNSeUBEY24ND55yFqzPROfACLcBGAsYHQ/w400-h70/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="400" /></a></div><div><br /></div><div>以行動電源為例子,行動電源內部電源可能是12v,經過DC-DC Converter之後是5v,輸出接的手機,手機就是負載。</div><div><br /></div><div>前面提到在充電部份(階段),電容會充電,直到需要的電壓。</div><div>而負載放電,會持續的從電容消耗電力,因為電容的電容量很小,通常非常短的時間(1~3秒內)就會消耗完全部電力,如下圖:</div><div class="separator" style="clear: both; text-align: left;"><a href="https://1.bp.blogspot.com/-7K08sOfmw30/X4FlLSwyhRI/AAAAAAAAuso/4VHudS_WZac30eF9tjmEmXOToo_R17CuwCLcBGAsYHQ/s588/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="232" data-original-width="588" height="252" src="https://1.bp.blogspot.com/-7K08sOfmw30/X4FlLSwyhRI/AAAAAAAAuso/4VHudS_WZac30eF9tjmEmXOToo_R17CuwCLcBGAsYHQ/w640-h252/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="640" /></a></div><br /><div>為了持續持續提供負載用電,讓電容維持在需要的電壓,因此整個DC-DC Converter會是充電、電池、放電3個階段加在一起,持續運作。</div><div class="separator" style="clear: both; text-align: left;"><a href="https://1.bp.blogspot.com/-bAwRG40TdeA/X4FsYaHap5I/AAAAAAAAutA/JFsztKAj7Xc8clH9gIu4brn-zf0nb6igwCLcBGAsYHQ/s931/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25962.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="249" data-original-width="931" height="172" src="https://1.bp.blogspot.com/-bAwRG40TdeA/X4FsYaHap5I/AAAAAAAAutA/JFsztKAj7Xc8clH9gIu4brn-zf0nb6igwCLcBGAsYHQ/w640-h172/%25E9%259B%25BB%25E5%258A%259B%25E9%259B%25BB%25E5%25AD%2590%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%25962.png" width="640" /></a></div><div><br /></div><div>為了維持9v輸出,通常DC-DC Converter內部會有個輸出電壓偵測器,偵測器會持續偵測電容上的電壓,讓它維持在輸出電壓(這裡的例子是9v),當到達9v時,就會讓開關電路關閉,當低於9v時,會讓開關電路開著,開關電路在開著狀態時,會持續的開關切換,進行充電。</div><div><br /></div><div>開關電路是常見的架構,也是主要的框架,看起來複雜,但其實後面每個架構都是延伸。</div><br />Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-84059362189059290632020-10-10T13:40:00.001+08:002020-10-10T13:40:14.646+08:00我認知的電力電子 - 1<div style="text-align: left;">多年前因為想自製行動電源和要買UPS,以及研究太陽能發電,那時研究了下DC-DC Converter、UPS原理、逆變器原理,<a href="http://ycfu.blog.mypc.tw/2008/11/eeepceeepc.html">自己買元件實做了交換式電源</a>和行動電源,差點自己DIY UPS。</div><div style="text-align: left;">既然前面提到變頻器,就把知道的部份整理出來。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">電力電子簡述:</div><div style="text-align: left;">以往電機俗稱大電,都是110v以上電壓,大多是交流電。</div><div style="text-align: left;">電子俗稱小電,都是3.3v、5v這種,常見的最多就48v。</div><div style="text-align: left;">更直白的說法,電機的電會電到人甚至電死人,電子的電通常不會電到人。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">以往電機歸電機,電子歸電子,電子元件都mA計算,電機都A計算,但因為電子元件耐電流能力提高了很多,有能力控制A等級,因此現代電機陸續都由電子元件驅動,最典型的應該就是變頻馬達控制。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">那時候為了自製DC-DC Converter,發現新式的電源供應器換電源轉換器都是交換式電源,因此研究了下,後來看UPS和太陽能發電才發現,原來都是同一技術的延伸。</div><div style="text-align: left;">(下篇待續)</div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-52531875698181865932020-10-10T11:54:00.005+08:002020-10-10T12:17:13.337+08:00抽水馬達(三相馬達)啟動與控制(Y-△啟動電路)<div style="text-align: left;"><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">最近因為大樓抽水馬達配電盤出問題,正好學習了解,並嘗試用樹莓派自製控制器。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">為了怕自己忘了,把已知的部份陸續紀錄。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">抽水馬達(三相馬達)控制(Y-△啟動電路):</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">三相馬達又稱三相感應馬達,基本上比較大型的馬達都是三相馬達,小的包括冷氣、冰箱,大的包括抽水馬達、電梯、電動車、電聯車應該都是三相馬達。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">馬達基本原理簡述:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">馬達內部就磁鐵和電磁鐵,電磁鐵通電產生磁力,和磁鐵發生互吸互斥,產生旋轉,持續的改變電磁鐵狀態,就能產生持續的旋轉。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">一般三相馬達都接交流電,因為上面提到,電磁鐵狀態持續改變,就能產生持續旋轉,交流電是有頻率的,台灣固定60Hz,這表示交流電會以60Hz的頻率自動改變狀態,就能讓馬達持續旋轉。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">而三相馬達共6條線,2條1組,形成3個電磁鐵。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">Y-△啟動電路用途:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">因為馬達啟動會需要大電流,為了避免控制電路在啟動時燒毀,因此需要改變馬達接法,降低啟動電流。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">參考:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><a href="http://163.28.10.78/content/vocation/ee/tp_nh/content2/plc/base/ydstart.htm">http://163.28.10.78/content/vocation/ee/tp_nh/content2/plc/base/ydstart.htm</a></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><a href="https://waterpower88.pixnet.net/blog/post/37076767">https://waterpower88.pixnet.net/blog/post/37076767</a></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">方法是啟動時使用Y接法,啟動後△接法,透過Y接法降低馬達運轉的效率,降低啟動電流。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">延伸,變頻器用途:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">事實上,馬達正常運作就是用△接法,會用Y型接法就只是為了啟動降低啟動電流,最新的作法是直接用△接法接馬達,但透過控制頻率的方式,頻率由低而高線性的調整,控制馬達轉速,同時降低啟動電流和運轉速度。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">這裡,我因為要用樹莓派控制,不打算做變頻器和用變頻器,因此使用傳統的Y-△啟動電路。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">控制說明:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">先看圖:</div><div class="separator" style="clear: both; text-align: left;"><a href="https://1.bp.blogspot.com/-rJBIvRW1QSE/X4EnxLhDv8I/AAAAAAAAurM/d0qXarIbFIYOT3S5-jW9I7lBWQNsJ5GeQCLcBGAsYHQ/s770/Y-%25E2%2596%25B3%25E5%2595%259F%25E5%258B%2595%25E9%259B%25BB%25E8%25B7%25AF%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="770" data-original-width="483" height="640" src="https://1.bp.blogspot.com/-rJBIvRW1QSE/X4EnxLhDv8I/AAAAAAAAurM/d0qXarIbFIYOT3S5-jW9I7lBWQNsJ5GeQCLcBGAsYHQ/w402-h640/Y-%25E2%2596%25B3%25E5%2595%259F%25E5%258B%2595%25E9%259B%25BB%25E8%25B7%25AF%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="402" /></a></div><br /><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">名詞說明:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">三相電源:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">習慣上會用R、S、T表示三相三條線,三相電源就是電源火線有3條,有三相3線式(3P3W),和三相4線式(3P4W),三相4線式第4條是接地線(水線)。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">三相電源有分三相220v和3相380v。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">三相3線式(3P3W)的任意2條火線,都會形成交流電,三相220v,R-S形成220v、S-T形成220v、R-T形成220v。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">三相馬達(三相感應馬達):</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">習慣上會用U1(1)、V1(3)、W1(5)和U2(2)、V2(4)、W2(6)表示三相馬達的3組線,U1(1)-U2(2)形成1組電磁鐵、V1(3)-V2(4)形成1組電磁鐵、W1(5)-W2(6)形成1組電磁鐵,(數字)表示第幾條線。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">U1(1) V1(3) W1(5)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">U2(2) V2(4) W2(6)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">接線:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">先描述正常△接法:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">R-U1(1)-W2(6)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">S-V1(3)-U2(2)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">T-W1(5)-V2(4)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">Y接法:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">R-U1(1)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">S-V1(3)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">T-W1(5)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">U2(2)-V2(4)-W2(6)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">樹莓派控制接法:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">需要3顆3相固態繼電器(SSR),以上圖來說,為左一、右上、右下。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">左一用60A的原因是,原先大樓的配電盤,左一電磁開關出現「咿-咿-咿-咿-咿-咿-咿」的聲音,因此換個大點的繼電器。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">左一來源接R、S、T,輸出接U1(1)、V1(3)、W1(5)</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">右上(我俗稱小白兔),負責控制Y接法,來源接U2(2)、V2(4)、W2(6),輸出三條線接一起。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">右下,負責控制△接法,來源接R、S、T,輸出接W2(6)、U2(2)、V2(4)。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">樹莓派控制:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">馬達停止時:</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">左一關閉、右上關閉、右下關閉。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">馬達啟動時(低速運轉)(Y接法):</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">左一啟動、右上啟動、右下關閉。</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><br /></div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">馬達正常運轉(全速運轉)(△接法):</div><div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;"><div>左一啟動、右上關閉,右下啟動。</div><div><br /></div><div>在這裡,樹莓派的功能其實就是替代原先的時間繼電器,差別是</div><div>1. 導入樹莓派能做到全數位控制</div><div>2. 電磁開關和時間繼電器改用SSR,變成無接點控制,原先220v的控制都改為直流電控制,電磁開關改用SSR無接點無火花,能提高系統壽命</div><div><br /></div><div>樹莓派內有PWM,SSR切換速度極快(ms等級)哪天想不開,說不定能魔改成變頻控制。</div><div><br /></div></div></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-50249306452135210112020-10-10T10:38:00.005+08:002020-10-10T10:45:06.091+08:00大樓上水塔、下水池狀態感測器(61F-G1)說明<div style="text-align: left;">最近因為大樓抽水馬達配電盤出問題,正好學習了解,並嘗試用樹莓派自製控制器。</div><div style="text-align: left;">為了怕自己忘了,把已知的部份陸續紀錄。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">61F-G1水位偵測器:</div><div style="text-align: left;">大廈大樓普遍使用的水位控制器,是歐姆龍出品的,好像是水電技術士考試內容。</div><div style="text-align: left;">有多個型號,G為單1組感測控制器,G1為2組串連,G2為3組串連。</div><div style="text-align: left;">大樓通常有下水池接自來水,上水塔和下水池水管連通接抽水馬達,所以通常用G1,2組感測器。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">61F-G和61F-G1的說明,我主要參考:</div><div style="text-align: left;"><a href="https://home.gamer.com.tw/creationDetail.php?sn=2202523">https://home.gamer.com.tw/creationDetail.php?sn=2202523</a></div><div style="text-align: left;"><a href="https://www.mobile01.com/topicdetail.php?f=168&t=5632523&p=2">https://www.mobile01.com/topicdetail.php?f=168&t=5632523&p=2</a></div><div style="text-align: left;"><br /></div><div style="text-align: left;">感測器稱為Unit,簡稱U。</div><div style="text-align: left;">G型號:</div><div style="text-align: left;">水位感測(輸入Input)接E1、E2、E3</div><div style="text-align: left;">提供的狀態(輸出Output)為Ta、Tb、Tc</div><div style="text-align: left;"><br /></div><div style="text-align: left;">G1型號:</div><div style="text-align: left;">G1型號因為串連</div><div style="text-align: left;"><div>水位感測(輸入Input),U1(下水池)接E1'、E2'、E3',U2(上水塔)接E1、E2、E3</div><div>提供的狀態(輸出Output),為Ta1、Tb1、Tb2、Tc2</div><div><br /></div></div><div style="text-align: left;">G1的各個情況和接線示意圖如下:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-h3EpVx0N13I/X4EgFAzOTqI/AAAAAAAAurA/L3cBtZ1_LVk1eMAENqw26Ghbu1eworafQCLcBGAsYHQ/s959/61F-G1%25E6%258E%25A5%25E7%25B7%259A%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="477" data-original-width="959" height="318" src="https://1.bp.blogspot.com/-h3EpVx0N13I/X4EgFAzOTqI/AAAAAAAAurA/L3cBtZ1_LVk1eMAENqw26Ghbu1eworafQCLcBGAsYHQ/w640-h318/61F-G1%25E6%258E%25A5%25E7%25B7%259A%25E7%25A4%25BA%25E6%2584%258F%25E5%259C%2596.png" width="640" /></a></div><br /><div style="text-align: left;">61F-G1內部幾個關鍵點:</div><div style="text-align: left;">1. 61F-G1內部把Tc1和Ta2互連,這2個IO並沒有提供</div><div style="text-align: left;">2. 61F-G1內部的繼電器狀態切換,都是Tc,U1是Tc2,U2是Tc1</div><div style="text-align: left;">3. U1表示下水池,在右側,U2表示上水塔,在左側</div><div style="text-align: left;">4. U1的繼電器IO均為2,Ta2、Tb2、Tc2,U2的繼電器IO均為1,Ta1、Tb1、Tc1</div><div style="text-align: left;"><br /></div><div style="text-align: left;">因為我要用樹莓派感測和控制抽水馬達,因此接線上,Tc2接GND,Ta1、Tb1、Tb2接GPIO。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">水位感測的原理,E1、E2、E3:</div><div style="text-align: left;">參考:</div><div style="text-align: left;"><a href="https://home.gamer.com.tw/creationDetail.php?sn=2202523">https://home.gamer.com.tw/creationDetail.php?sn=2202523</a></div><div style="text-align: left;"><br /></div><div style="text-align: left;">E1接在高水位,E2接在低水位,E3接在水池底。</div><div style="text-align: left;">因為水會造成短路(導通),因此:</div><div style="text-align: left;">E1-E3導通時,表示高水位</div><div style="text-align: left;">E2-E3導通時,表示低水位</div><div style="text-align: left;">因為高水位時,E1、E2和E3都會導通,因此偵測上,E1-E3、E2-E3應該是反向關係,</div><div style="text-align: left;">E1-E3導通時輸出0(短路),E2-E3輸出1(開路)</div><div style="text-align: left;">E1-E3輸出1(開路),E2-E3導通輸出0(短路)</div><div style="text-align: left;"><br /></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-7131699173668624672020-10-05T03:54:00.005+08:002020-10-05T03:58:11.284+08:00保險之我見 - 10 保費、保金總額<div style="text-align: left;">一般來說,我們認知的保險,我花100元買,得到最多1,000的保障,</div><div style="text-align: left;">那麼我花1,000元買,應該能得到最多10,000的保障吧?</div><div style="text-align: left;">不一定~</div><div style="text-align: left;"><br /></div><div style="text-align: left;">前一篇提到,保險公司內部有精算師,所有保單推出前,都會精算過,</div><div style="text-align: left;">每張保單的合約書中,都會有保單的計算公式,</div><div style="text-align: left;">也都會設計一張試算表,提供給業務試算保額(要繳的金額、保障的保險總金額、甚至可能有傭金金額)。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">我們一般認知上,繳的保費和保單總價值應該如下圖:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Tmuw60xkp8U/X3oiHrHD7XI/AAAAAAAAunM/UqmjbDrL3JEli9l90BqLF0eOc7924JmSwCLcBGAsYHQ/s603/snapshot1.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="373" data-original-width="603" height="396" src="https://1.bp.blogspot.com/-Tmuw60xkp8U/X3oiHrHD7XI/AAAAAAAAunM/UqmjbDrL3JEli9l90BqLF0eOc7924JmSwCLcBGAsYHQ/w640-h396/snapshot1.png" width="640" /></a></div><br /><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><br /><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;">實際上,如果拿保單上的公式試算會發現,實際上可能是下圖:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-rxkDXdx2XAU/X3olNSr-kDI/AAAAAAAAunY/1gaJllm24nIG0qundcpogiHNnlkTkNbKgCLcBGAsYHQ/s603/snapshot1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="373" data-original-width="603" height="396" src="https://1.bp.blogspot.com/-rxkDXdx2XAU/X3olNSr-kDI/AAAAAAAAunY/1gaJllm24nIG0qundcpogiHNnlkTkNbKgCLcBGAsYHQ/w640-h396/snapshot1.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">年繳保費越多,保單總價值會上升,但上升並不是等比級數,</div><div class="separator" style="clear: both; text-align: left;">這原因很簡單,保險公司要設定最大可承擔風險,</div>因此年繳最多保費,是有最大保障沒錯,但並不是等比例的保障。<br /><br /></div><div style="text-align: left;">因此,一個衡量是,買這張保單,要的是最大效益,還是最大保障?</div><div style="text-align: left;">效益和保障間的取捨關係</div><div style="text-align: left;">如果同類型商品,在多間保險公司買多張低價格的保單,如何?</div><div style="text-align: left;"><br /></div><div style="text-align: left;">另一個保費和保金關係的點 - 年期</div><div style="text-align: left;">目前一般終身險,通常20年期,一年也許30,000~50,000之間,</div><div style="text-align: left;">因為這是大部分人認為1年繳1個月薪水差不多的金額,</div><div style="text-align: left;">20年期很簡單,直接把金額x20,要繳交的總金額約略在70萬上下(通常在80幾萬)。</div><div style="text-align: left;">但,有注意到,年期可以選擇的......可以15年繳清,10年繳清,6年繳清。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">10年繳清,每年繳大約50,000~70,000之間,</div><div style="text-align: left;">直接把金額x10,要繳交的總金額約略在50萬上下(通常在60萬左右),</div><div style="text-align: left;">差10年週期,總金額大概少繳20萬左右。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">微妙的是,年期越短,保險附加項目可能減少,</div><div style="text-align: left;">例如,10年期以下沒有包含豁免條款,為何?</div><div style="text-align: left;">因為成本阿..........</div><div style="text-align: left;"><br /></div><div style="text-align: left;">但大部份人不會注意這些,心態上:</div><div style="text-align: left;">1. 預設值直接選</div><div style="text-align: left;">2. 一年繳個30,000~50,000,大概1個月薪水(看收入高低),感覺還ok</div><div style="text-align: left;">3. 根本沒想過保費還有繳交總金額試算這回事,就每年繳固定金額阿</div><div style="text-align: left;"><br /></div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-17914515433187254492020-10-05T03:13:00.004+08:002020-10-05T03:13:31.945+08:00保險之我見 - 9保險公司的所有保單,都會先在內部由精算師精算設計後,送政府核定。<div>但政府也知道保險公司要賺錢,畢竟大量投資要靠保險公司,</div><div>但也知道不能讓保險公司賺太兇,會影響保戶權益,因此會給予限制和審核,</div><div>其中一個近年被提出的,是所謂的「現金流量」,</div><div>要求保險公司所有資金中,必須有一定比例是現金,現金水位需要佔整體的幾成,</div><div>我如果沒記錯,要求是60%以上是現金,</div><div>因此,保險公司需要投資能夠持續產出現金的項目,</div><div>這其中,保險公司最愛的,就是商辦.........</div><div>稍微觀察會發現,商業區的商辦、土地、科技園區的辦公大廈,</div><div>很多都是保險公司的,為何?</div><div>因為每個月都能收租金,租金是「現金」。</div><div>這能讓保險公司符合金管會現金流量的規定,又不會滿手現金沒投資。</div><div><br /></div><div>因此老實說,保險公司要經營到倒很難,通常都是亂投資導致鉅額虧損,</div><div>以經營的角度看保險公司,錢從保戶來,保戶的錢拿來投資,</div><div>投資出問題,損害的是保戶權益,聽起來就是完全轉嫁,也是個能賺錢的模式,</div><div>現金多金額大,又能成為投資大戶受禮遇,</div><div>因為現金多、金額大,在市場上,直接佔據主導地位,算是造市者之一。</div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com1tag:blogger.com,1999:blog-10062204.post-6399042690285127002020-10-05T02:59:00.007+08:002020-10-05T02:59:50.106+08:00保險之我見 - 8 投資型保單<div style="text-align: left;">投資型保單是種很奇怪的商品,它的設計是保單的保價金讓保戶投資基金。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">但這裡有幾個問題:</div><div style="text-align: left;">1. 保價金和投資連動</div><div style="text-align: left;">投資失利,則保險保障受損</div><div style="text-align: left;">大部分人對投資型保單會火大的最主要原因是因為,大部分人會把保單解約的時期,</div><div style="text-align: left;">通常是景氣差的時候,景氣差需要現金,因此把保單解約,很合理,</div><div style="text-align: left;">但投資型保單因為和投資連動,景氣差時通常投資虧損嚴重,</div><div style="text-align: left;">虧損嚴重又同時需要現金,就發現保險保價金少了很多,就直接火了,</div><div style="text-align: left;">當然,多年前的連動債事件,造成大量投資型保單失效,是導火線。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">2. 基金的可選擇性</div><div style="text-align: left;">投資型保單能投資的基金通常有限定,</div><div style="text-align: left;">基金公司也要賺阿,所以這張保單等於多了一塊成本,</div><div style="text-align: left;">而且基金的成本通常是買或賣時繳手續費(台灣),</div><div style="text-align: left;">這等於每次轉換都會產生隱藏成本減少保單價值。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">這麼怪的商品,保險公司怎麼會這麼愛賣?</div><div style="text-align: left;">一般來說,保險公司收了保戶的錢之後,通常拿來投資,</div><div style="text-align: left;">各國的保險公司(包含公營私營保險公司)通常都是國際基金大客戶,</div><div style="text-align: left;">也都是各國國內重要的投資大戶,</div><div style="text-align: left;">股票、基金、政府公債、公司債券、房地產...等投資商品,</div><div style="text-align: left;">保險公司幾乎都是主要投資者之一,</div><div style="text-align: left;">因此對保險公司來說,保險公司需要承擔投資風險,</div><div style="text-align: left;">投資型保單對於保險公司來說,等於是把投資風險完全轉嫁到保戶自己,</div><div style="text-align: left;">同時間,又能從基金公司賺到介紹費或手續費,</div><div style="text-align: left;">對於保險公司來說,是很棒的商品,</div><div style="text-align: left;">因此保險公司在景氣榮景時,股市基金大好的情況下,</div><div style="text-align: left;">都會推投資型保單,因為這時候基金公司要賣基金。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">但反過來看保戶,</div><div style="text-align: left;">保戶同時要承擔投資風險、支付投資手續費、原本保費的成本也攤提在裡面,</div><div style="text-align: left;">等於是同時承擔了很多項成本,不算是好的商品。</div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0tag:blogger.com,1999:blog-10062204.post-10735917226283273332020-10-05T01:48:00.002+08:002020-10-05T01:48:28.680+08:00保險之我見 - 7 外幣<div style="text-align: left;">最近幾年,儲蓄險變形除了低高利率之外,就是外幣。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">外幣之所以複雜,因為它關係到匯率,而匯率基本上跟國際局勢和政情有關,</div><div style="text-align: left;">因為匯率是各國央行干預的。</div><div style="text-align: left;"><br /></div><div style="text-align: left;">通常,保險公司大推外幣保單,會是在那個外幣在大幅度貶值的情況下,</div><div style="text-align: left;">例如,美元對台幣,1:28元</div><div style="text-align: left;"><br /></div><div style="text-align: left;">一般來說,匯率會升降,在大幅貶值(大幅降)時銷售,大家會覺得有利可圖,</div><div style="text-align: left;">可以賺到匯差,因此購買,但保險公司沒說的是,匯率貶值是一時的,</div><div style="text-align: left;">保費要繳6年10年,除非有能力一次把外幣換完每年扣款,否則不是躉繳其實不合算,</div><div style="text-align: left;">但一次換完,為啥不直接外幣定存?而要買張外幣保單?</div><div style="text-align: left;">另一種情況,澳幣、南非幣</div><div style="text-align: left;">澳幣、南非幣是特殊的,它不一定會升值,有可能貶值後升不上去,</div><div style="text-align: left;">這很可能跟國家央行政策或該國貨幣策略相關。</div>Codyhttp://www.blogger.com/profile/17029531215338378145noreply@blogger.com0