What’s new in Swift 5?

原文出處: https://learnappmaking.com/swift-5-whats-new/

Filter And Count With “count(where:)”

在 iOS 中我們很常需要從一個列表中有條件的選擇元素並計算個數,例如:

let scores = [1, 3, 8, 2, 5, 6, 2, 10]
let count = scores.filter({ $0 > 5 }).count
print(count)  // output 3

但這樣有兩個缺點:

  • 太囉唆(too verbose):我們先做了 filtering,接著我們才做 counting,但我們實際上只想做 counting,在閱讀上也很容易忽略掉 .count
  • 太浪費(too wasted):我們做了一個中介運算 filter(_:),但這個運算結果被遺棄了(discarded),這就像我們從一個水果籃複製一個蘋果,只為了算『蘋果一個』,接著我們就丟掉它了

因此我們有了 count(where:) (SE-0220),只在一個運算中做 filter 和 count

let scores = [1, 3, 8, 2, 5, 6, 2, 10]
let count = scores.count(where: { $0 > 5 })
print(count) // Output: 3

isMultiple(of: )

很多時候我們會需要測試 一個數是否可以被另一個數整除(divisible) 此時我們可能會這樣寫:

let result = 5 % 2 == 0
if result {
	print("5 is even")
} else {
	print("5 is odd")
}

SE-0225 提出了一新的函數實作 isMultiple(of:)給 integer 變數,用來檢查該變數是否是另一個整數的倍數:

let number = 42
if number.isMultiple(of: 2) {
	print("\\(number) is even")
}

這樣寫有比較簡單吧!

這樣寫有幾個好處:

  • 比較接近一般英語的語法:if number is multiple of 2,因此增加了可讀性
  • XCode 可以探索到 functions,可以避免開發者沒注意到使用了 %
  • 因為 % 運算元造成的錯誤並不罕見,何況在不同程式語言中 % 的實作還有所不同

Result 型態

SE-0235 中,為 Swift 新加入了一個資料型態 Result

你可以有 2-3 種方式做例外處理,例如 do-try-catch, try?, guard,社群中因此發展出了 Result type 的用法,這個資料型態封裝了兩種 state:success 和 failure,而因為這個資料型態太廣為使用,因此被收錄進了 Swift 的標準函式庫,以下是提案中提及的解決方案:

public enum Result<Success, Failure: Error> {
    case success(Success), failure(Failure)
}

上述方法定義了一個 enumeration 包含了 .success.failure 兩個案例,這兩個案例各有一個關聯的型態 SuccessFailure,所有的資料型態在這裡都是 generic,因此 Success 可以是任何值,但你傳遞給 Failure 的值必須符合 Error 這個 protocol。

簡單來說,Result 資料型態可以在非同步函數中用來當作參數傳遞:

dataTask(with: url) { (result: Result<Data, Error>) in
	switch result {
    case let .success(data):
        handleResponse(data: data)
    case let .error(error):
        handleError(error)
    }
}

The Result type encapsulates possible return values and errors in one object. It also uses the power of enumerations to help you write expressive code, i.e. a switch block that gracefully deals with return values and errors.

This change to Swift is definitely more involved than just adding a new convenient function, so it’s worth it to investigate it further. You could even call it philosophical: the Swift language designers actively discuss how the language is used, as opposed to just providing Swift’s syntax.

Handling Future Enum Cases

SE-0192 針對以下議題提出一個解決方案:

  • 當你對 enum 使用 switch 時,你需要列舉窮盡所有 case
  • 你未來有可能需要增加一個 case 到 enum 中,這對 switch 來說是一個 code-breaking change (意思是這樣的改變會導致 switch code 被 break 而不能用)

由於 switching enums 需要窮盡列舉,因此當你想要增加一個新的 case 到 enum,原本已經寫好在用的 switch 就會爛掉而不能用,因為你必須也新增這個新的 enum case 到 switch-case 中。

這對 libraries, SDKs 和 frameworks 來說都是很笨重的,因為每次你新增一個 case 進去,你就可能毀了別人寫的 code,此外,code-breaking change 還可能對相容性有負面影響。

例如以下的 enum:

enum Fruit {
    case apple
    case orange
    case banana
}

然後我們用 switch 來列舉:

let fruit = ...

switch fruit {
case .apple:
    print("Purchasing apple for $0.50")
case .orange:
    print("Purchasing orange for $0.39")
case default:
    print("We don't sell that kind of fruit here.")
}

我們加入了 default 這個 case,因此這個 switch 敘述有達到窮盡列舉,然而,假如你沒有使用 default,而是僅僅將 .apple, .orange.banana 列舉,接著之後你或其他開發者在 Fruit 中新增了一個新的水果,就會導致你的 switch code 炸掉。

這個作法有兩個部分:

  • Enumerations in the Swift standard library, and imported from elsewhere, can be either frozen or non-frozen. A frozen enumeration cannot change in the future. A non-frozen enum can change in the future, so you’ll need to deal with that.
  • When switching over a non-frozen enum, i.e. one that can change in the future, you should include a “catch-all” default case that matches any values that the switch doesn’t already explicitly match. If you don’t use default when you need to, you’ll get a warning.

這會衍生出另外一個問題:如果你有不想包含的 case,但你沒有想到,你怎麼能確定這個 default 有排除掉你可能並不想要包含的值?或是有包含到之後可能被加進 Fruit 的 case 呢?Swift compiler 也不會知道,因此 compiler 並不會提醒你有沒列舉到的 case。

假設你現在新增了一個 .pineappleFruit 中,你要如何知道我們可以賣這個水果但我們不會這麼做?又或者你要怎麼知道這是一個由 framework 新加入的水果,而且我們可以賣它?

在 Swift 5.0 中,可以在 default 這個 case 中加入一個新的 keyword @unknown,這樣並不會改變原本我們理解的 default 行為:

switch fruit {
case .apple:
    ...
case @unknown default:
    print("We don't sell that kind of fruit here.")
}

@unknown 是用來在 Xcode 中觸發 warning 的,當你正在處理有潛在為窮盡列舉的 switch 敘述時,警告就會被觸發。

Flatten Nested Optionals With “try?”

Nested optionals are… strange. Here’s an example:

let number:Int?? = 5
print(number)
// Output: 5

The above number constant is doubly wrapped in an optional, and its type is Int?? or Optional<Optional<Int>>. Although it’s OK to have nested optionals, it’s also downright confusing and unnecessary.

Swift has a few ways to avoid accidentally ending up with nested optionals, for example in casting with as? and in optional chaining. However, when using try? to convert errors to optionals, you can still end up with nested optionals.

Here’s an example:

let car:Car? = ...
let engine   = try? car?.getEngine()

In the above example, car is an optional of type Car?. On the second line, we’re using optional chaining, because car is an optional. The return value of the expression car?.getEngine() is optional too, because of the optional chaining. It’s type is Engine?.

這個例子中的 car是 optional type Car?,第二行我們那樣寫是 optional chaining,因為 car 是 optional,而 car?.getEngine() 的回傳值也是 optional,且這個回傳值的資料型態是 Engine?

當你結合了 try?,它的返回值是 optional,你因此會得的 double/nested optional,因此 engine 最後的型態是 Engine??(因為 car?.getEngine() 回傳的 Engine?,被 try? 再次包裝成 Engine??),這就會產生問題了,因為你必須要 unwrap 兩次才能得 engine 真正的值

因為 as? 已經 flattens optionals,一個脫離 nested optional 的方法是採用以下的方式:

if let engine = (try? car?.getEngine()) as? Engine {
    // OMG!
}

這段程式碼將 Engine?? 轉型(cast)成 Engine,因為 as? 的關係而 flatten optionals 了,轉型本身是沒有用的,因為我們要的是 Engine。

既然如此,為什麼不直接讓 try? 也具有 as? 的功能(flatten optionals),因此在 SE-0230 中就提出了解決方案:

It flattens nested optionals resulting from try?, giving it the same behavior as as?and optional chaining.

The New “compactMapValues()” Function For Dictionaries

The Swift standard library includes two useful functions for arrays and dictionaries:

  • The map(_:) function applies a function to array items, and returns the resulting array, while the compactMap(_:) function also discards array items that are nil
  • The mapValues(_:) function does the same for dictionaries, i.e. it applies a function to dictionary values, and returns the resulting dictionary – but it doesn’t have a nil discarding counterpart

SE-0218 修改了函示庫,並為 dictionaries 加入了compactMapValues(_:) 函式,這個 function 結合了 array的 compactMap(_: ) 和 dictionaries 的 mapValues,更有效率的 mapping-and-filtering dictionary 的值

考慮一個情境,當你要調查你家成員的年齡(integer)時,你那愚笨的叔叔 Bob 拼出了他的年紀而非數字,因此你可能會得到以下的程式碼內容:

let ages = [
    "Mary": "42",
    "Bob": "twenty-five har har har!!",
    "Alice": "39",
    "John": "22"
]

let filteredAges = ages.compactMapValues({ Int($0) })
print(filteredAges)
// Output: ["Mary": 42, "Alice": 39, "John": 22]

compactMapValues(_:) 在這個情境中可以有用的移除 dictionary 中的 nil 值,或是當你正在使用 failable initializers 來轉換 dictionary values 也很有用。

練習在 WPF 中加入 Entity Framework 資料繫結建立簡單的資料應用程式

參考教學:https://docs.microsoft.com/zh-tw/visualstudio/data-tools/create-a-simple-data-application-with-wpf-and-entity-framework-6?view=vs-2017

建立資料庫與範例資料集

  1. 在 Visual Studio 中開啟SQL Server 物件總管視窗。 (SQL Server 物件總管安裝的一部分資料儲存和處理中的工作負載Visual Studio 安裝程式。)依序展開SQL Server節點。 以滑鼠右鍵按一下您的 LocalDB 執行個體,然後選取新的查詢。
  2. 查詢編輯器視窗隨即開啟。
  3. 複製 Northwind 的 TRANSACT-SQL 指令碼到剪貼簿。 這個 T-SQL 指令碼會從頭建立 Northwind 資料庫,並填入資料。
  4. 將 T-SQL 指令碼貼到查詢編輯器,然後選擇Execute 按鈕。
  5. 短時間之後,查詢完成執行,並建立 Northwind 資料庫。
  6. 加入新連接northwind。

專案

準備好一個 C# WPF 專案後

  1. 接下來,針對 Entity Framework 6 中新增 NuGet 套件。 在 [方案總管] 中,選取 [專案] 節點。 在主功能表中,選擇專案 > 管理 NuGet 套件。
    管理 NuGet 套件 功能表項目

2. 在 NuGet 套件管理員,按一下瀏覽連結。 Entity Framework 可能是最上層的套件清單中。 按一下 安裝右窗格中,並遵循提示。 [輸出] 視窗會告訴您完成安裝。
Entity Framework NuGet 套件

3. 現在您可以使用 Visual Studio 來建立模型,根據 Northwind 資料庫。

建立模型

  1. 在方案總管的專案節點上按一下滑鼠右鍵,然後選擇 加入 > 新項目。 在左窗格中,在C#節點,選擇資料,然後在中間窗格中,選擇 ADO.NET 實體資料模型。

2. 將此模型命名為 Northwind_model,然後選擇 確定。

3. [實體資料模型精靈] 隨即開啟。 選擇資料庫的 EF Designer ,然後按一下下一步。

4. 在下一個畫面中,選擇您的 LocalDB Northwind 連接,然後按一下 [下一步]。

5. 在精靈的下一個頁面中,選擇哪些資料表、 預存程序,以及要包含在 Entity Framework 模型中其他資料庫物件。 展開樹狀檢視中的 [dbo] 節點,然後選擇 Customers、Orders、Order Details。 保留選取預設值,然後按一下完成。

精靈會產生C#表示 Entity Framework 模型的類別。 類別是純舊C#類別,而且它們是我們繫結至 WPF 使用者介面。 .Edmx 檔案會描述關聯性和其他中繼資料與資料庫中的物件產生關聯的類別。 .Tt 檔案會產生運作模型,並將變更儲存到資料庫的程式碼的 T4 範本。 您可以看到所有這些檔案中的方案總管 中Northwind_model 節點下:

設計工具介面 .edmx檔案可讓您修改一些屬性和模型中的關聯性。 我們不會在此使用設計工具。

6. .Tt 檔案是 general purpose,您需要調整其中一個,才能使用 WPF 資料繫結(which requires ObservableCollections)。在 方案總管,展開 Northwind_model 節點,直到您找到Northwind_model.tt,開啟這個檔案以查看與編輯以下其中一個:

  • Replace the two occurrences ICollection (line 303, 491)with ObservableCollection<T>
  • Replace the first occurrence HashSet<T> with ObservableCollection<T> around line 51. Do not replace the second occurrence fo HashSet(around 606).
  • Replace the only occurrence of System.Collections.Generic (arond line 431) with System.Collections.ObjectModel.

(有碰過只改第三個還是會建置失敗的狀況,最安全的做法是三個都改)

7. 按下 Ctrl+Shift+B 來建置專案。 When the build finishes, the model classes are visible to the data sources wizard.

現在您已準備好將連結至 XAML 頁面的這個模型,讓您可以檢視、 巡覽及修改資料。

Databind the model to the XAML page

  1. 從主功能表中,選擇專案 > 新增新的資料來源以顯示資料來源組態精靈。

2. 選擇 物件,因為您要繫結模型類別,不是針對資料庫:

3. 選取 客戶。 (Sources fro Orders are automatically generated from the Orders navigation property in Customer.)

4. 按一下 [ 完成]。

此時就已經可以在 MainWindow.cs 中使用 Model class 了

5. 回到 MainWindow.xaml 的 CodeView,把以下內容貼到 <Grid></Grid>

<Grid.RowDefinitions>
  <RowDefinition Height="auto"/>
  <RowDefinition Height="auto"/>
  <RowDefinition Height="*"/>
</Grid.RowDefinitions>

6. 按下 shift + alt + D 叫出 Data Source,按一下 Customers 右方倒三角按鈕,點選 詳細資料

7. 接著再點一下 Customers,但是按著他拖曳到 MainWindow.xaml 的圖形化視窗中,此時精靈就會自動加入一個編輯元件

8. 試試在程式碼中以 ORM 方式存取客戶資料

9. 按下 F5 執行看輸出

WinForm/WPF 中切換畫面

Windows 的視窗程式最陽春的擴充視窗空間方式是跳出新的視窗(sub-window),如果想要在同一個視窗中做切換要怎麼做呢? 參考這個影片教學,這裡我用 WPF demo

建立一個空 WPF 專案後,在 MainWindow 中加入一個 frame,名稱改為 mainFrame,屬性 NavigationUIVisibility 設為 Hidden,Content 建議設為空

把 frame 的配置 (layout) 設定為全部填滿 (full area)

在專案中新增兩個 頁面(WPF),英文是 Page,名稱分別是 Page1.xaml 和 Page2.xaml,並在其中各加入一個 label 和 button

撰寫控制程式碼

MainWindow.xaml.cs

public MainWindow()
{
    InitializeComponent();

    // 設定初始化載入的 Page 為 Page 1
    Page1 p1 = new Page1();
    mainFrame.NavigationService.Navigate(p1);
}

Page1.xaml.cs
雙點擊按鈕以便自動建立 click 事件

private void Button_Click(object sender, RoutedEventArgs e)
{
    Page2 p2 = new Page2();
    this.NavigationService.Navigate(p2);
}

Page2.xaml.cs
雙點擊按鈕以便自動建立 click 事件

private void Button_Click(object sender, RoutedEventArgs e)
{
    Page1 p1 = new Page1();
    this.NavigationService.Navigate(p1);
}

接著就可以執行看看效果了:

如果前面沒有隱藏 NavigationUIVisibility

XCode 中為 macOS Command Line Tool 專案加入 Unit Test

XCTest module 是 XCode 內建提供的單元測試框架模組,如果是建立一個 iOS App 或 macOS Cococa App 這種帶有視窗畫面的應用程式專案,XCode 會自動在創建專案時一併建立測試專案,但如果在 XCode 中建立一個 macOS Command Line Tool,預設是不會自動建立 XCTest Unit Test 的

如果直接在專案中新增一個 Unit Test 檔案,會發生 Connot load underlying module for 'XCTest'

正確做法是在左邊的 Project Navigator 中,切換到 “Show the Test navigator",小圖示是:

接著在最左下方點選 + 的符號,選擇第一個 New Unit Test Target...

輸入 Test class name

按下 Finish 後,就可以在 Show the Project navigator (左上第一個資料夾圖示) 中看到多了一個 Group、Test class 和 Info.plist,其中 Test class 已經繼承了 XCTestCase,import 的 XCTest 模組也沒有錯誤了

接著進入上方的 Scheme 選單,點選 Edit scheme,編輯當前的 scheme,在 Test 的地方加入 Test target

然後在 show the Test navigator 中就會出現新增的 Unit Test 了

在 A.swift 使用 B.swift 的 class

BeImported.swift

public class BeImported {
  static func show_message() -> Void {
    print("Jello!")
  }
}

main.swift

(BeImported.show_message())

In swift, you can’t import a typical *.swift file.

Swift 中的 import 是對 module 有效,不是檔案

所以如果你要在 main.swift 中使用 BeImport.swift 的 class:

Works

$ xcrun -sdk macosx swiftc -emit-executable -o a BeImported.swift main.swift
$ ./a
Hello!

$ swiftc -o a main.swift BeImported.swift
$ ./a
Hello!

Not Works

# swift main.swift BeImported.swift
(Got some errors..)

因為 swift 是 Swift executer,要用 swiftc 先 compile 才能執行

(Java 好像也是這樣? 但 javac 編譯好後,要用 java 執行,而 swift a 會錯誤)

為 xshogi 官網的第二個 domain 加入 ssl

實驗域名:xshogi.com

已經有了上次的經驗後,要為另一個 xshogi.com 加入 ssl 憑證,過程記錄如下。

第一階段:購買 SSL 憑證

  1. openssl req -nodes -newkey rsa:2048 -keyout xshogi.com.key -out xshogi.com.csr
↪ openssl req -nodes -newkey rsa:2048 -sha256 -keyout xshogi.com.key -out xshogi.com.csr
Generating a 2048 bit RSA private key
............................+++
............+++
writing new private key to 'xshogi.com.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:TW
State or Province Name (full name) []:Taipei
Locality Name (eg, city) []:city
Organization Name (eg, company) []:xshogi
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:www.xshogi.com
Email Address []:xshogi.it@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
  1. 到 Gandi SSL 購買介面,選擇類型為 “基本" 和 “單一位址",然後到貼上 CSR 的地方

這邊如果把上次的貼上(in .ssl/xshogi_heroku.csr),會解析簽署名稱(CN)成 http://www.xshogi.com.tw,所以要貼上新的 xshogi.com.csr

  1. 下一步以後付完款,接著就是等待 域名所有權驗證

會收到一封信說,有三種方法可以驗證您擁有域名。您可以在憑證控制面板中的 “編輯" 選擇要驗證的方式。

經由DNS 認證
必須將此介面提供的 CNAME 記錄加入 xshogi.com 的 DNS 區域檔。若為多重域名憑證,則每個域名都必須完成此操作。第一次驗證的時間將會在三小時之後開始。

經由電子郵件認證(最快)
重要:您必須建立一個電子郵件地址 admin@xshogi.com (若為多重域名憑證,每個域名都需要一個電子郵件地址) 以接收認證訊息 (內含需點擊的連結,會連線到 Comod 的網站)。注意:若電子郵件地址建立速度太慢,可能會漏收郵件:也可以於此介面重新發送認證電子郵件。

檔案認證
您必須到憑證詳細資訊中取得認證的 .TXT 檔並且把它放置到需驗證網站的根目錄。若為多重域名憑證,則上述的每個域名與網站都必須完成此操作。

若完全不需進行這些操作,這表示您是使用免費的憑證並且由 Gandi 自動驗證。

我選擇使用 DNS:

切到區域檔紀錄,會發現已經自動幫我們加入一筆新的 CNAME 了

過一陣子後驗證就會自動完成並啟用了。

  1. 下載 .crt 和 .pem 並將他們連接在一起 cat GandiStandardSSLCA2.pem http://www.xshogi.com.crt > all_xshogi.com.crt

第二階段:設定 Heroku

  1. 新增 certification 和 key 到 heroku
↪ heroku certs:add all_xshogi.com.crt xshogi.com.key --app xshogi
 ›   Warning: heroku update available from 7.16.4 to 7.16.6
Resolving trust chain... done
Adding SSL certificate to ⬢ xshogi... !
 ▸    Only one SNI endpoint is allowed per app (try certs:update instead).

Oops! 因為已經有一組了,只能用 update 來替換掉原本的

↪ heroku certs:update all_xshogi.com.crt xshogi.com.key --app xshogi
 ›   Warning: heroku update available from 7.16.4 to 7.16.6
Resolving trust chain... done
 ▸    Potentially Destructive Action
 ▸    This command will change the certificate of endpoint ceratosaurus-25252 from ⬢ xshogi.
 ▸    To proceed, type xshogi or re-run this command with --confirm xshogi

> xshogi
Updating SSL certificate ceratosaurus-25252 for ⬢ xshogi... done
Updated certificate details:
Common Name(s): www.xshogi.com
                xshogi.com
Expires At:     2019-10-26 07:59 UTC
Issuer:         /C=FR/ST=Paris/L=Paris/O=Gandi/CN=Gandi Standard SSL CA 2
Starts At:      2018-10-25 08:00 UTC
Subject:        /OU=Domain Control Validated/OU=Gandi Standard SSL/CN=www.xshogi.com
SSL certificate is verified by a root authority.

還要在 add 一次:

↪ heroku certs:add all_xshogi.com.crt xshogi.com.key --app xshogi
 ›   Warning: heroku update available from 7.16.4 to 7.16.6
Resolving trust chain... done
Adding SSL certificate to ⬢ xshogi... done
Certificate details:
Common Name(s): www.xshogi.com
                xshogi.com
Expires At:     2019-10-26 07:59 UTC
Issuer:         /C=FR/ST=Paris/L=Paris/O=Gandi/CN=Gandi Standard SSL CA 2
Starts At:      2018-10-25 08:00 UTC
Subject:        /OU=Domain Control Validated/OU=Gandi Standard SSL/CN=www.xshogi.com
SSL certificate is verified by a root authority.

=== The following common names already have domain entries
www.xshogi.com
xshogi.com

=== Your certificate has been added successfully.  Update your application's DNS settings as follows
Domain             Record Type  DNS Target
─────────────────  ───────────  ──────────────────────────────────────────────────────────────
www.xshogi.com     CNAME        marine-procompsognathus-l8xr054mis0egvrf5l7q8pks.herokudns.com
www.山角行.com        CNAME        www.xn--rhto43ho7a.com.herokudns.com
xshogi.com.tw      ALIAS/ANAME  xshogi.com.tw.herokudns.com
www.xshogi.com.tw  CNAME        www.xshogi.com.tw.herokudns.com
山角行.com            ALIAS/ANAME  xn--rhto43ho7a.com.herokudns.com
xshogi.com         ALIAS/ANAME  opaque-owl-6ikxb7mtjg47hg8u6mcu5g2x.herokudns.com
  1. 檢查憑證
↪ heroku certs:info -a xshogi
 ›   Warning: heroku update available from 7.16.4 to 7.16.6
Fetching SSL certificate velociraptor-96982 info for ⬢ xshogi... done
Certificate details:
Common Name(s): www.xshogi.com
                xshogi.com
Expires At:     2019-10-26 07:59 UTC
Issuer:         /C=FR/ST=Paris/L=Paris/O=Gandi/CN=Gandi Standard SSL CA 2
Starts At:      2018-10-25 08:00 UTC
Subject:        /OU=Domain Control Validated/OU=Gandi Standard SSL/CN=www.xshogi.com
SSL certificate is verified by a root authority.
  1. 修改 Gandi 的網頁轉址使用 https

Result

Porting FreeRTOS to STM32F429Discovery

工具:

  • stlink:用來燒錄和 debug 的工具(a chip)
  • openocd:用來執行 GDB server 與啟用 ARM semihosting
  • gcc-arm-none-eabi: ARM GCC
  • FreeRTOS source code
  • Board: STM32F429 Discovery

OS X 上安裝 gcc-arm-none-eabi、stlink、openocd

方法一

$ brew tap PX4/homebrew-px4    
$ brew update    
$ sudo brew install gcc-arm-none-eabi    (might not the latest version)

方法二

  1. Download “Mac installation tarball"
  2. Decompress tar
  3. set PATH of gcc-arm-none-eabi

Porting

Porting FreeRTOS 到 STM32F429-Discovery 主要是有幾個重點:

  • Utility: 來自 STM32F429I-Discovery_FW_V1.0.1(官方 driver),用途是提供一些操作硬體周邊的函式庫(API),例如 LCD
  • FreeRTOS:FreeRTOS 的 source code,在下載回來的包中 FreeRTOSV8.2.1/FreeRTOS/Source
  • CORTEX_M4F_SK: 在下載回來的包中 FreeRTOSV8.2.1/FreeRTOS/Demo,用途是提供平台 CORTEX_M4F_SK 上的驅動函式庫,是屬於 FreeRTOS 軟體方開發的接口

應用程式的開發

以 Lab39 的 freertos-basic 中 FreeRTOS 應用程式原始碼 src/ 與 include/ 為例,我們整合成 app/,並把 src/ 中的 main.c 單獨拉出來,延續 myFreeRTOS (branch: game) 的檔案架構下,應用程式 game/ 和 main.c 是放在這裡 CORTEX_M4F_SK 中,所以專案結構如下:

.
|___CORTEX_M4F_SK
|               |__ app/
|               |__ main.c
|               |__ others C files
|___ FreeRTOS
|___ Utility

Hint:main.c 有樣板,在 STM32F429I-Discovery_FW_V1.0.1/Projects/Template

參考