13、nRF52xx藍牙學習(GPIOTE組件方式的任務配置)

下面再來探討下驅動庫如何實現任務的配置,驅動庫的實現步驟應該和寄存器方式對應,關
鍵點就是如何調用驅動庫的函數。
本例里同樣的對比寄存器方式編寫兩路的 GPOITE 任務輸出,一路配置為輸出翻轉,一路設
置為輸出低電平。和 GPIOTE 事件相反,初始化任務應該是輸出,同時需要使能任務和觸發任務的 驅動庫函數。下面介紹下如下三個組件庫函數:
(1)nrfx_gpiote_out_init 函數
nrfx_err_t nrfx_gpiote_out_init(nrfx_gpiote_pin_t                pin,nrfx_gpiote_out_config_t const * p_config)
{NRFX_ASSERT(nrf_gpio_pin_present_check(pin));NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_INITIALIZED);NRFX_ASSERT(p_config);nrfx_err_t err_code = NRFX_SUCCESS;if (pin_in_use(pin)){err_code = NRFX_ERROR_INVALID_STATE;}else{if (p_config->task_pin){int8_t channel = channel_port_alloc(pin, NULL, true);if (channel != NO_CHANNELS){nrf_gpiote_task_configure((uint32_t)channel,pin,p_config->action,p_config->init_state);}else{err_code = NRFX_ERROR_NO_MEM;}}else{pin_in_use_set(pin);}if (err_code == NRFX_SUCCESS){if (p_config->init_state == NRF_GPIOTE_INITIAL_VALUE_HIGH){nrf_gpio_pin_set(pin);}else{nrf_gpio_pin_clear(pin);}nrf_gpio_cfg_output(pin);pin_configured_set(pin);}}NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));return err_code;
}

nrfx_gpiote_out_init 函數的主要作用是初始化一個 GPIO 引腳作為 GPIOTE(通用外設中斷和事件)的輸出引腳。它接收一個引腳編號和一個配置結構體指針作為參數,根據配置對引腳進行相應的初始化設置,并返回初始化結果的錯誤碼。

函數參數 ? nrfx_gpiote_pin_t pin:要初始化的 GPIO 引腳編號。

? nrfx_gpiote_out_config_t const * p_config:指向 GPIO 輸出配置結構體的指針,該結構體包含了引腳的各種配置信息,如是否作為任務引腳、引腳動作、初始狀態等。

nrfx_gpiote_out_config_結構體定義如下 :

typedef struct
{nrf_gpiote_polarity_t action;     /**< Configuration of the pin task. */nrf_gpiote_outinit_t  init_state; /**< Initial state of the output pin. */bool                  task_pin;   /**< True if the pin is controlled by a GPIOTE task. */
} nrfx_gpiote_out_config_t;

其中nrf_gpiote_outinit_t是枚舉類型,其定義如下 :

typedef enum
{NRF_GPIOTE_INITIAL_VALUE_LOW  = GPIOTE_CONFIG_OUTINIT_Low,       ///<  Low to high.NRF_GPIOTE_INITIAL_VALUE_HIGH = GPIOTE_CONFIG_OUTINIT_High       ///<  High to low.
} nrf_gpiote_outinit_t;

NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
? ? NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_INITIALIZED);
? ? NRFX_ASSERT(p_config);


NRFX_ASSERT 是一個斷言宏,用于在開發和調試階段檢查某些條件是否滿足。如果條件不滿足,程序會觸發斷言失敗,幫助開發者快速定位問題。

nrf_gpio_pin_present_check(pin) 檢查指定的引腳是否存在

?m_cb.state == NRFX_DRV_STATE_INITIALIZED 檢查 GPIOTE 驅動的狀態是否已經初始化。

?p_config 檢查配置結構體指針是否有效。


if (pin_in_use(pin))
? ? {
? ? ? ? err_code = NRFX_ERROR_INVALID_STATE;
? ? }


pin_in_use(pin) 函數檢查指定的引腳是否已經被使用。如果該引腳已經被使用,則將錯誤碼設置為 NRFX_ERROR_INVALID_STATE,表示狀態無效。


else
? ? {
? ? ? ? if (p_config->task_pin)
? ? ? ? {
? ? ? ? ? ? int8_t channel = channel_port_alloc(pin, NULL, true);

? ? ? ? ? ? if (channel != NO_CHANNELS)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? nrf_gpiote_task_configure((uint32_t)channel,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pin,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? p_config->action,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? p_config->init_state);
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? err_code = NRFX_ERROR_NO_MEM;
? ? ? ? ? ? }
? ? ? ? }


如果引腳未被使用,檢查配置結構體中的 task_pin 字段。

如果 task_pin 為真,表示該引腳要作為任務引腳使用。

channel_port_alloc(pin, NULL, true) 函數嘗試為該引腳分配一個 GPIOTE 通道。如果分配成功,返回通道編號;如果沒有可用通道,返回 NO_CHANNELS。

如果通道分配成功,調用 nrf_gpiote_task_configure 函數對該通道進行配置,傳入通道編號、引腳編號、引腳動作和初始狀態等參數。

如果通道分配失敗,將錯誤碼設置為 NRFX_ERROR_NO_MEM,表示內存不足(這里實際是沒有可用的 GPIOTE 通道)。


channel_port_alloc函數代碼如下:
static int8_t channel_port_alloc(uint32_t pin, nrfx_gpiote_evt_handler_t handler, bool channel)
{int8_t   channel_id = NO_CHANNELS;uint32_t i;uint32_t start_idx = channel ? 0 : GPIOTE_CH_NUM;uint32_t end_idx   =channel ? GPIOTE_CH_NUM : (GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS);// critical sectionfor (i = start_idx; i < end_idx; i++){if (m_cb.handlers[i] == FORBIDDEN_HANDLER_ADDRESS){pin_in_use_by_te_set(pin, i, handler, channel);channel_id = i;break;}}// critical sectionreturn channel_id;
}

代碼解釋如下 :

函數概述 channel_port_alloc 是一個靜態函數,其作用是為指定的引腳分配一個通道。

該函數會在特定的通道范圍內查找可用通道,若找到,就將該通道分配給指定引腳,并返回通道編號;若未找到,就返回 NO_CHANNELS。

函數參數

? pin:類型為 uint32_t,代表要分配通道的引腳編號。

? handler:類型為 nrfx_gpiote_evt_handler_t,是一個事件處理函數指針,當該引腳發生事件時會調用此函數。

? channel:類型為 bool,用于確定通道分配的范圍。若為 true,就在普通通道范圍內分配;若為 false,就在低功耗事件通道范圍內分配。 ?

若成功分配通道,返回通道編號;若未找到可用通道,返回 NO_CHANNELS。

代碼詳細解釋


static int8_t channel_port_alloc(uint32_t pin, nrfx_gpiote_evt_handler_t handler, bool channel)
{
? ? int8_t ? channel_id = NO_CHANNELS;
? ? uint32_t i;

? ??


根據 channel 參數確定通道分配的起始和結束索引

uint32_t start_idx = channel ? 0 : GPIOTE_CH_NUM; 這行代碼的作用是根據 channel 的值來確定通道分配范圍的起始索引。

? 若 channel 為 true(非零),則 start_idx 被賦值為 0。這意味著通道分配從編號為 0 的通道開始。

? 若 channel 為 false(零),則 start_idx 被賦值為 GPIOTE_CH_NUM。這表明通道分配從編號為 GPIOTE_CH_NUM 的通道開始。

uint32_t end_idx = channel ? GPIOTE_CH_NUM : (GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS);

這行代碼的作用是根據 channel 的值來確定通道分配范圍的結束索引。

? 若 channel 為 true(非零),則 end_idx 被賦值為 GPIOTE_CH_NUM。這意味著通道分配的范圍是從 0 到 GPIOTE_CH_NUM - 1。

? 若 channel 為 false(零),則 end_idx 被賦值為 GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS。這表明通道分配的范圍是從 GPIOTE_CH_NUM 到 GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS - 1。

總結 這兩行代碼的目的是根據 channel 的值選擇不同的通道分配范圍:

? 當 channel 為 true 時,通道分配范圍是從 0 到 GPIOTE_CH_NUM - 1,通常代表普通通道的范圍。

? 當 channel 為 false 時,通道分配范圍是從 GPIOTE_CH_NUM 到 GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS - 1,一般代表低功耗事件通道的范圍。



? ??

? ? // 進入臨界區,這里代碼注釋表示是臨界區,但代碼中未實際體現臨界區的保護代碼
? ? // 臨界區用于確保在多線程或多任務環境下,對共享資源的操作是原子的

? ? // 遍歷指定范圍的通道
? ? for (i = start_idx; i < end_idx; i++)
? ? {
? ? ? ? // 檢查當前通道的處理函數地址是否為禁止使用的地址
? ? ? ? if (m_cb.handlers[i] == FORBIDDEN_HANDLER_ADDRESS)
? ? ? ? {
? ? ? ? ? ? // 如果是,調用 pin_in_use_by_te_set 函數將該引腳標記為已使用,并關聯處理函數
? ? ? ? ? ? pin_in_use_by_te_set(pin, i, handler, channel);
? ? ? ? ? ? // 將通道編號賦值給 channel_id
? ? ? ? ? ? channel_id = i;
? ? ? ? ? ? // 找到可用通道后,跳出循環
? ? ? ? ? ? break;
? ? ? ? }
? ? }
? ? // 退出臨界區,同樣這里代碼注釋表示是臨界區,但未實際體現臨界區的保護代碼

? ? // 返回通道編號,如果未找到可用通道,返回 NO_CHANNELS
? ? return channel_id;
}
? ?代碼邏輯總結

1. 把 channel_id 初始化為 NO_CHANNELS,這意味著默認情況下未找到可用通道。

2. 依據 channel 參數確定通道分配的起始和結束索引。

3. 進入臨界區(代碼注釋表明是臨界區,但未實際實現臨界區保護),以此保證在多線程或多任務環境下對共享資源的操作是原子的。

4. 遍歷指定范圍的通道,檢查每個通道的處理函數地址是否為禁止使用的地址。

5. 若找到可用通道,調用 pin_in_use_by_te_set 函數將該引腳標記為已使用,并關聯處理函數,然后將通道編號賦值給 channel_id,接著跳出循環。

6. 退出臨界區(代碼注釋表明是臨界區,但未實際實現臨界區保護)。

7. 返回通道編號,若未找到可用通道,返回 NO_CHANNELS。? ? ? ??


nrf_gpiote_task_configure函數代碼如下

__STATIC_INLINE void nrf_gpiote_task_configure(uint32_t idx, uint32_t pin,nrf_gpiote_polarity_t polarity,nrf_gpiote_outinit_t  init_val)
{NRF_GPIOTE->CONFIG[idx] &= ~(GPIOTE_CONFIG_PORT_PIN_Msk |GPIOTE_CONFIG_POLARITY_Msk |GPIOTE_CONFIG_OUTINIT_Msk);NRF_GPIOTE->CONFIG[idx] |= ((pin << GPIOTE_CONFIG_PSEL_Pos) & GPIOTE_CONFIG_PORT_PIN_Msk) |((polarity << GPIOTE_CONFIG_POLARITY_Pos) & GPIOTE_CONFIG_POLARITY_Msk) |((init_val << GPIOTE_CONFIG_OUTINIT_Pos) & GPIOTE_CONFIG_OUTINIT_Msk);
}

?nrf_gpiote_task_configure 是一個靜態內聯函數,其用途是對 nRF GPIOTE(通用引腳輸入 / 輸出任務和事件)模塊的特定通道配置寄存器進行設置。借助該函數,你能夠指定特定通道所關聯的引腳、引腳極性以及初始輸出值。

函數參數

? idx:uint32_t 類型,代表要配置的 GPIOTE 通道的索引。

? pin:uint32_t 類型,代表要關聯到該通道的引腳編號。

? polarity:nrf_gpiote_polarity_t 類型,用于指定引腳的極性,例如高電平觸發、低電平觸發等。 ? init_val:nrf_gpiote_outinit_t 類型,用于指定引腳的初始輸出值。 ?


? ?
? ?代碼邏輯步驟

1. ?清除相關位:

?NRF_GPIOTE->CONFIG[idx] &= ~(GPIOTE_CONFIG_PORT_PIN_Msk |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?GPIOTE_CONFIG_POLARITY_Msk |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?GPIOTE_CONFIG_OUTINIT_Msk);
? ?
??NRF_GPIOTE->CONFIG[idx] 表示 GPIOTE 模塊中第 idx 個通道的配置寄存器。 ?GPIOTE_CONFIG_PORT_PIN_Msk、GPIOTE_CONFIG_POLARITY_Msk 和 GPIOTE_CONFIG_OUTINIT_Msk 分別是用于選擇引腳、極性和初始輸出值的位掩碼。

?~ 是按位取反運算符,&= 是按位與賦值運算符。這行代碼的作用是把配置寄存器中與引腳、極性和初始輸出值相關的位清零,為后續設置新值做準備。 ?

2. ?設置新值:

NRF_GPIOTE->CONFIG[idx] |= ((pin << GPIOTE_CONFIG_PSEL_Pos) & GPIOTE_CONFIG_PORT_PIN_Msk) |
? ? ? ? ? ? ? ? ? ? ? ? ? ?((polarity << GPIOTE_CONFIG_POLARITY_Pos) & GPIOTE_CONFIG_POLARITY_Msk) |
? ? ? ? ? ? ? ? ? ? ? ? ? ?((init_val << GPIOTE_CONFIG_OUTINIT_Pos) & GPIOTE_CONFIG_OUTINIT_Msk);
? ?
??<< 是左移運算符,用于把 pin、polarity 和 init_val 移動到配置寄存器中對應的位置。

& 是按位與運算符,用于確保只有對應位掩碼覆蓋的位被設置。

| 是按位或運算符,|= 是按位或賦值運算符。這行代碼的作用是將移動并掩碼處理后的 pin、polarity 和 init_val 值設置到配置寄存器中。 ? ?

總結 此函數的主要功能是先清除 GPIOTE 通道配置寄存器中與引腳、極性和初始輸出值相關的位,然后將新的引腳編號、極性和初始輸出值設置到這些位上,從而完成對指定通道的配置。? ?

? ? ?


(2)nrfx_gpiote_out_task_enable函數

void nrfx_gpiote_out_task_enable(nrfx_gpiote_pin_t pin)
{NRFX_ASSERT(nrf_gpio_pin_present_check(pin));NRFX_ASSERT(pin_in_use(pin));NRFX_ASSERT(pin_in_use_by_te(pin));nrf_gpiote_task_enable((uint32_t)m_cb.pin_assignments[pin]);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/77588.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77588.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/77588.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java的基本語法(1)

一、運算符和表達式 舉例說明什么是運算符&#xff0c;什么是表達式&#xff1a; int a 1; int b 2; int c a b; 在這個例子當中&#xff0c;是運算符&#xff0c;并且是算術運算符 ab是表達式&#xff0c;因為是運算符&#xff0c;所以ab是算術表達式 1.1算術運算符 …

C++學習之密碼學知識

目錄 1.文檔介紹 2.知識點概述 3.項目準備 4.序列化介紹 5.項目中基礎組件介紹 6.基礎模塊在項目中作用 7.項目中其他模塊介紹 8.加密三要素 9.對稱加密和非堆成加密 10.對稱和非對稱加密特點 11.堆成加密算法des 12.des對稱加密算法 13.對稱加密算法aes 14.知識點…

安裝vllm

ubuntu 22.04, RTX3080, cuda 12.1, cudnn 8.9.7&#xff0c;cuda和cudnn的安裝參考&#xff1a;https://blog.csdn.net/m0_52111823/article/details/147154526?spm1001.2014.3001.5501。 查看版本對應關系&#xff0c;下載12.1對應的whl包&#xff0c;https://github.com/vl…

【WPF】自定義控件:ShellEditControl-同列單元格編輯支持文本框、下拉框和彈窗

需要實現表格同一列&#xff0c;單元格可以使用文本框直接輸入編輯、下拉框選擇和彈窗&#xff0c;文本框只能輸入數字&#xff0c;彈窗中的數據是若干位的二進制值。 本文提供了兩種實現單元格編輯狀態下&#xff0c;不同編輯控件的方法&#xff1a; 1、DataTrigger控制控件的…

Gson、Fastjson 和 Jackson 對比解析

目錄 1. Gson (Google) 基本介紹&#xff1a; 核心功能&#xff1a; 特點&#xff1a; 使用場景&#xff1a; 2. Fastjson (Alibaba) 基本介紹&#xff1a; 核心功能&#xff1a; 特點&#xff1a; 使用場景&#xff1a; 3. Jackson 基本介紹&#xff1a; 核心功能…

淺談微信視頻號推薦算法

這次可能會稍微有點干貨&#xff0c;但保證不晦澀~ 一、算法推薦的本質&#xff1a;猜你喜歡 vs 社交綁架? 視頻號的推薦系統本質上在做兩件事&#xff1a; ?預測你的興趣?&#xff1a;通過你的瀏覽、點贊、評論、分享等行為&#xff0c;分析你的偏好。?滿足社交需求?&…

halcon模板匹配(一)create_shape_model_xld

目錄 一、提取剎車盤孔洞輪廓二、形狀模板的創建-設置-訓練-查找三、找到亮的圓孔四、獲得匹配結果五、使用use_polarity進行模板匹配六、計算四個圓對應的矩形框七、創建四個圓對應的模板并查找一、提取剎車盤孔洞輪廓 小技巧總結,使用boundary 函數提取區域邊界,在邊界范圍…

day26圖像處理OpenCV

文章目錄 一、OpenCV1.介紹2.下載3.圖像的表示4.圖像的基本操作4.1圖片讀取或創建4.1.1讀取4.1.2創建 4.2創建窗口4.3顯示圖片4.3.1設置讀取的圖片4.3.2設置顯示多久4.3.3釋放 4.4.保存圖片4.5圖片切片&#xff08;剪裁&#xff09;4.6圖片大小調節 5.在圖像中繪值5.1繪制直線5…

零基礎開始學習鴻蒙開發-智能家居APP離線版介紹

目錄 1.我的小屋 2.查找設備 3.個人主頁 前言 好久不發博文了&#xff0c;最近都忙于面試&#xff0c;忙于找工作&#xff0c;這段時間終于找到工作了。我對鴻蒙開發的激情依然沒有減退&#xff0c;前幾天做了一個鴻蒙的APP&#xff0c;現在給大家分享一下&#xff01; 具體…

C++的*了又*

先看下面一段代碼 class HeapWord {friend class VMStructs;private:char *i; };主函數 #include "HeapWord.hpp" int main() {HeapWord *heapword new HeapWord();HeapWord *p new HeapWord();HeapWord **p1 new HeapWord *();heapword 3;*(HeapWord **)p he…

yolov8在windows系統的C++版本的onnxruntime部署方法

1.各個軟件的的環境需要保持在統一的版本。 onnxruntime需要和cuda的版本對應上,版本號:onnxruntime-win-x64-gpu-1.18.1 ,鏈接: NVIDIA - CUDA | onnxruntime cuda:本機顯卡支持的版本,cuda11.7,鏈接:CUDA Toolkit Archive | NVIDIA Developer cudnn:需要對應到cud…

js chrome 插件,下載微博視頻

修改說明&#xff1a; 代碼資源&#xff0c;免積分下載 起因&#xff0c; 目的: 最初是想下載微博上的NBA視頻&#xff0c;因為在看網頁上看視頻很不方便&#xff0c;快進一次是10秒&#xff0c;而本地 VLC 播放器&#xff0c;快進一次是5秒。另外我還想做點視頻剪輯。 對比…

【vue3】@click函數傳動態變量參數

根據java的學習&#xff0c;摸索了一下vue3 函數傳參的方式。以此作為記錄。有更好的其它方式&#xff0c;可以評論區補充。 <script> const tmpref(); </script><button click"tmpFunction(傳遞參數:tmp)">按鈕</button> // 直接【字符串…

jmeter 集成ZAP進行接口測試中的安全掃描 實現方案

以下是將 JMeter 集成 ZAP(OWASP Zed Attack Proxy)進行接口測試中安全掃描的實現方案: 1. 環境準備 JMeter 安裝:從 JMeter 官方網站(https://jmeter.apache.org/download_jmeter.cgi)下載并安裝 JMeter,確保其版本穩定。ZAP 安裝:從 ZAP 官方網站(https://www.zapr…

全能格式轉換器v16.3.0.159綠色便攜版

前言 全能格式轉換器具有音視頻格式轉換、合并視頻、壓縮視頻、錄制視頻、下載視頻、DVD刻錄等功能。以超快的轉換速度及強大的功能在國外名聲大噪&#xff0c;轉換速度是市面同類產品的30倍&#xff0c;操作簡便&#xff0c;支持158種視頻格式無損轉換&#xff0c;批量轉換高…

【基于開源insightface的人臉檢測,人臉識別初步測試】

簡介 InsightFace是一個基于深度學習的開源人臉識別項目,由螞蟻金服的深度學習團隊開發。該項目提供了人臉檢測、人臉特征提取、人臉識別等功能,支持多種操作系統和深度學習框架。本文將詳細介紹如何在Ubuntu系統上安裝和實戰InsightFace項目。 目前github有非常多的人臉識…

設計一個簡單的權限管理系統

針對大規模服務器集群的權限管理系統設計&#xff0c;需結合 角色分層、最小權限原則 和 動態權限控制 來實現安全高效的權限管理。以下是分階段設計方案&#xff1a; 一、核心設計思路 基于角色的訪問控制&#xff08;RBAC&#xff09; 定義角色層級&#xff08;如董事長 >…

使用 nano 文本編輯器修改 ~/.bashrc 文件與一些快捷鍵

目錄 使用 nano 編輯器保存并關閉文件使用 sed 命令直接修改文件驗證更改 如果你正在使用 nano 文本編輯器來修改 ~/.bashrc 文件&#xff0c;以下是保存并關閉文件的具體步驟&#xff1a; 使用 nano 編輯器保存并關閉文件 打開 ~/.bashrc 文件 在終端中運行以下命令&#xf…

spm12_fMRI 2*4混合方差分析 Flexible factorial 對比矩陣

實驗設計&#xff1a;2*4被試內設計 分析模型&#xff1a;spm 二階分析中的 Flexible factorial 問題&#xff1a;Flexible factorial交互作用對比矩陣如何編寫&#xff1f; 老師&#xff1a;deepseek老師【大神們看看這個矩陣是否可以如下編寫&#xff1f;】 以下是來自de…

用Python修改字體字形與提取矢量數據:fontTools實戰指南

字體設計與分析是NLP和視覺領域的交叉應用&#xff0c;而**fontTools** 是一款強大的Python庫&#xff0c;可以讓我們直接操作字體文件的底層結構。本文將通過兩個實用函數&#xff0c;展示如何修改特定字形和提取所有字形的矢量數據&#xff0c;幫助開發者快速上手字體編輯與分…