FreeRTOS進階篇5---FreeRTOS隊列分析
- 歡迎使用Markdown編輯器
-
- 新的改變
- 功能快捷鍵
- 合理的建立标題,有助于目錄的生成
- 如何改變文本的樣式
- 插傳入連結接與圖檔
- 如何插入一段漂亮的代碼片
- 生成一個适合你的清單
- 建立一個表格
-
- 設定内容居中、居左、居右
- SmartyPants
- 建立一個自定義清單
- 如何建立一個注腳
- 注釋也是必不可少的
- KaTeX數學公式
- 新的甘特圖功能,豐富你的文章
- UML 圖表
- FLowchart流程圖
- 導出與導入
-
- 導出
- 導入
https://blog.csdn.net/zhzht19861011/article/details/51510384
FreeRTOS提供了多種任務間通訊方式,包括:
任務通知(版本V8.2以及以上版本)
隊列
二進制信号量
計數信号量
互斥量
遞歸互斥量
其中,二進制信号量、計數信号量、互斥量和遞歸互斥量都是使用隊列來實作的,是以掌握隊列的運作機制,是很有必要的。
隊列是FreeRTOS主要的任務間通訊方式。可以在任務與任務間、中斷和任務間傳送資訊。發送到隊列的消息是通過拷貝實作的,這意味着隊列存儲的資料是原資料,而不是原資料的引用。先看一下隊列的資料結構:
typedef struct QueueDefinition
{
int8_t pcHead; / 指向隊列存儲區起始位置,即第一個隊列項 */
int8_t pcTail; / 指向隊列存儲區結束後的下一個位元組 */
int8_t pcWriteTo; / 指向下隊列存儲區的下一個空閑位置 */
union /* 使用聯合體用來確定兩個互斥的結構體成員不會同時出現 */
{
int8_t *pcReadFrom; /* 當結構體用于隊列時,這個字段指向出隊項目中的最後一個. */
UBaseType_t uxRecursiveCallCount;/* 當結構體用于互斥量時,用作計數器,儲存遞歸互斥量被"擷取"的次數. */
} u;
List_t xTasksWaitingToSend; /* 因為等待入隊而阻塞的任務清單,按照優先級順序存儲 */
List_t xTasksWaitingToReceive; /* 因為等待隊列項而阻塞的任務清單,按照優先級順序存儲 */
volatile UBaseType_t uxMessagesWaiting;/*< 目前隊列的隊列項數目 */
UBaseType_t uxLength; /* 隊列項的數目 */
UBaseType_t uxItemSize; /* 每個隊列項的大小 */
volatile BaseType_t xRxLock; /* 隊列上鎖後,存儲從隊列收到的清單項數目,如果隊列沒有上鎖,設定為queueUNLOCKED */
volatile BaseType_t xTxLock; /* 隊列上鎖後,存儲發送到隊列的清單項數目,如果隊列沒有上鎖,設定為queueUNLOCKED */
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
uint8_t ucStaticAllocationFlags;
#endif
} xQUEUE;
typedef xQUEUE Queue_t;
下面的所有API函數都是圍繞這個資料結構展開,是以資料結構的每個成員都需要了解。如果你是第一次看這篇文章,即使有注釋,可能你對結構體的某些成員還是不了解,不要着急,這是正常的。後面介紹API函數的時候,會一一使用這些成員,結合着具體執行個體,會很容了解的,你需要做的,是要反複翻到這裡檢視。
1.隊列建立函數
在《FreeRTOS系列第18篇---FreeRTOS隊列API函數》一文中,我們介紹了建立隊列API函數xQueueCreate(),但其實這是一個宏,隻是定義的像函數而已。真正被執行的函數是xQueueGenericCreate(),我們稱這個函數為通用隊列建立函數。 我們來分析一下xQueueGenericCreate()函數,函數原型為:
QueueHandle_t xQueueGenericCreate
(
const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t *pucQueueStorage,
StaticQueue_t *pxStaticQueue,
const uint8_t ucQueueType
)
uxQueueLength:隊列項數目
uxItemSize:每個隊列項的大小
pucQueueStorage:使用靜态配置設定隊列時才使用,指向定義隊列存儲空間,如果使用動态配置設定隊列空間(預設),向這個參數傳遞NULL。
pxStaticQueue:使用靜态配置設定隊列時才使用,指向隊列控制結構體,如果使用動态配置設定隊列空間(預設),向這個參數傳遞NULL。
ucQueueType:類型。可能的值為:
queueQUEUE_TYPE_BASE:表示隊列
queueQUEUE_TYPE_SET:表示隊列集合
queueQUEUE_TYPE_MUTEX:表示互斥量
queueQUEUE_TYPE_COUNTING_SEMAPHORE:表示計數信号量
queueQUEUE_TYPE_BINARY_SEMAPHORE:表示二進制信号量
queueQUEUE_TYPE_RECURSIVE_MUTEX :表示遞歸互斥量
然而,等下我們看源碼,就會看到,在xQueueGenericCreate()函數中,參數ucQueueType隻是用來可視化跟蹤調試用。
xQueueGenericCreate()函數的源碼如下所示:
QueueHandle_t xQueueGenericCreate(
const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t *pucQueueStorage,
StaticQueue_t *pxStaticQueue,
const uint8_t ucQueueType )
{
Queue_t *pxNewQueue;
/* 如果使能可視化跟蹤調試,這裡用來消除編譯器警告. */
( void ) ucQueueType;
/*配置設定隊列結構體和隊列項存儲空間.可以靜态也可以動态配置設定,取決于參數值,FreeRTOS預設采取動态配置設定 */
pxNewQueue = prvAllocateQueueMemory( uxQueueLength, uxItemSize, &pucQueueStorage, pxStaticQueue );
if( pxNewQueue != NULL )
{
if( uxItemSize == ( UBaseType_t ) 0 )
{
/* 沒有為隊列項存儲配置設定記憶體,但是pcHead指針不能設定為NULL,因為隊列用作互斥量時,pcHead要設定成NULL.這裡隻是将pcHead指向一個已知的區域 */
pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
}
else
{
/* 指向隊列項存儲區域*/
pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
}
/* 初始化隊列結構體成員*/
pxNewQueue->uxLength = uxQueueLength;
pxNewQueue->uxItemSize = uxItemSize;
( void ) xQueueGenericReset( pxNewQueue, pdTRUE );
#if ( configUSE_TRACE_FACILITY == 1 )
{
pxNewQueue->ucQueueType = ucQueueType;
}
#endif /* configUSE_TRACE_FACILITY */
traceQUEUE_CREATE( pxNewQueue );
}
return ( QueueHandle_t ) pxNewQueue;
}
我們以預設的動态配置設定隊列存儲空間方式講述一下隊列建立過程。首先調用函數prvAllocateQueueMemory配置設定隊列結構體和隊列項存儲空間,結構體和隊列項在存儲空間上是連續的,如圖1-1所示。
圖1-1:為隊列配置設定的記憶體
如果隊列記憶體申請成功,接下來會初始化隊列結構體成員,先是pcHead成員,然後是uxLength和uxItemSize成員,最後調用函數xQueueGenericReset()初始化剩下的結構體成員。
假設我們申請了3個隊列項,每個隊列項占用4位元組存儲空間(即uxLength=3、uxItemSize=4),則經過初始化後的隊列記憶體如圖1-2所示。(這個圖形象的描述了隊列結構體的大部分成員的作用)。
圖1-2:初始化後的隊列項記憶體
2.入隊
隊列項入隊也稱為投遞(Send),分為帶中斷保護的入隊操作和不帶中斷保護的入隊操作。每種情況下又分為從隊列尾部入隊和從隊列首部入隊兩種操作,從隊列尾部入隊還有一種特殊情況,覆寫式入隊,即隊列滿後自動覆寫最舊的隊列項。如表2-1所示。
表2-1:入隊API接口清單
2.1 xQueueGenericSend()
這個函數用于入隊操作,絕不可以用在中斷服務程式中。根據參數的不同,可以從隊列尾入隊、從隊列首入隊和覆寫式入隊。覆寫式入隊用于隻有一個隊列項的場合,入隊時如果隊列已滿,則将之前的隊列項覆寫掉。函數原型為:
BaseType_t xQueueGenericSend
(
QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition
)
xQueue:隊列句柄
pvItemToQueue:指針,指向要入隊的項目
xTicksToWait:如果隊列滿,等待隊列空閑的最大時間,如果隊列滿并且xTicksToWait被設定成0,函數立刻傳回。時間機關為系統節拍時鐘周期,宏portTICK_PERIOD_MS可以用來輔助計算真實延時值。如果INCLUDE_vTaskSuspend設定成1,并且指定延時為portMAX_DELAY将引起任務無限阻塞(沒有逾時)。
xCopyPosition:入隊位置,可以選擇從隊列尾入隊、從隊列首入隊和覆寫式入隊。
這個函數為了獲得最高效率而放寬了編碼标準:有多個傳回點。是以如果純粹以文字方式來講解,我覺得很難達到好的效果,是以我首先給出整理後的源碼(去除調試和隊列集合有關代碼),然後畫出流程圖,對函數的關鍵點做重點描述。
整理後的源碼:
BaseType_t xQueueGenericSend(
QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
for( ;; )
{
taskENTER_CRITICAL();
{
/* 隊列還有空閑?正在運作的任務一定要比等待通路隊列的任務優先級高.如果使用覆寫式入隊,則不需要關注隊列是否滿*/
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
/*完成資料拷貝工作,分為從隊列尾入隊,從隊列首入隊和覆寫式入隊*/
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
/* 如果有任務在此等待隊列資料到來,則将該任務解除阻塞*/
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
/*有任務因等待出隊而阻塞,則将任務從隊列等待接收清單中删除,然後加入到就緒清單*/
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* 解除阻塞的任務有更高的優先級,則目前任務要讓出CPU,是以觸發一個上下文切換.又因為現在還在臨界區,要等退出臨界區後,才會執行上下文切換.*/
queueYIELD_IF_USING_PREEMPTION();
}
}
else if( xYieldRequired != pdFALSE )
{
/* 這個分支處理特殊情況*/
queueYIELD_IF_USING_PREEMPTION();
}
taskEXIT_CRITICAL();
return pdPASS;
}
else
{
if( xTicksToWait == ( TickType_t ) 0 )
{
/* 如果隊列滿并且沒有設定逾時,則直接退出 */
taskEXIT_CRITICAL();
/* 傳回隊列滿錯誤碼 */
return errQUEUE_FULL;
}
else if( xEntryTimeSet == pdFALSE )
{
/* 隊列滿并且規定了阻塞時間,是以需要配置逾時結構體對象 */
vTaskSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
}
}
taskEXIT_CRITICAL();
/* 退出臨界區,至此,中斷和其它任務可以向這個隊列執行入隊(投遞)或出隊(讀取)操作.因為隊列滿,任務無法入隊,下面的代碼将目前任務将阻塞在這個隊列上,在這段代碼執行過程中我們需要挂起排程器,防止其它任務操作隊列事件清單;挂起排程器雖然可以禁止其它任務操作這個隊列,但并不能阻止中斷服務程式操作這個隊列,是以還需要将隊列上鎖,防止中斷程式讀取隊列後,使阻塞在出隊操作其它任務解除阻塞,執行上下文切換(因為排程器挂起後,不允許執行上下文切換) */
vTaskSuspendAll();
prvLockQueue( pxQueue );
/* 檢視任務的逾時時間是否到期 */
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
if( prvIsQueueFull( pxQueue ) != pdFALSE )
{
/*逾時時間未到期,并且隊列仍然滿*/
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
/* 解除隊列鎖,如果有任務要解除阻塞,則将任務移到挂起就緒清單中(因為目前排程器挂起,是以不能移到就緒清單)*/
prvUnlockQueue( pxQueue );
/* 恢複排程器,将任務從挂起就緒清單移到就緒清單中*/
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
}
else
{
/* 隊列有空閑,重試 */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else
{
/* 逾時時間到期,傳回隊列滿錯誤碼*/
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
}
}
程式流程如圖2-1所示,我們對圖中紅色字型标注的部分做詳解。
圖2-1:通用入隊操作流程圖
當任務将資料入隊時,如果隊列未滿或者以覆寫式入隊,情況是最簡單的,調用函數prvCopyDataToQueue()将要入隊的資料拷貝到隊列。這個函數處理三種入隊情況,第一種是隊列項大小為0時(即隊列結構體成員uxItemSize為0,比如二進制信号量和計數信号量),不進行資料拷貝工作,而是将隊列項計數器加1(即隊列結構體成員uxMessagesWaiting++);第二種情況是從隊列尾入隊時,則将資料拷貝到指針pxQueue->pcWriteTo指向的地方、更新指針指向的位置、隊列項計數器加1;第三種情況是從隊列首入隊時,則将資料拷貝到指針pxQueue->u.pcReadFrom指向的地方、更新指針指向的位置、隊列項計數器加1。如果是覆寫式入隊,還會調整隊列項計數器的值。
完成資料入隊操作後,還要檢查是否有任務因為等待出隊而阻塞,因為這次資料入隊,隊列至少有一個隊列項,如果有阻塞任務,則阻塞的最高優先級任務可以解除阻塞了。
因等待出隊而阻塞的任務會将任務的事件清單項(即任務TCB結構體成員xEventListItem,我們在《FreeRTOS進階篇2—FreeRTOS任務建立分析》一文中講到過事件清單項,它是任務TCB的一個結構體成員)挂接到隊列的等待出隊清單上(即隊列結構體成員xTasksWaitingToReceive)。現在,因為要解除任務阻塞,我們需要将任務的事件清單項從隊列的等待出隊隊列上删除,并且将任務移動到就緒清單中。這一切,都是調用函數xTaskRemoveFromEventList()實作的。
之後,如果解除阻塞的任務優先級比目前任務優先級更高,則觸發一個PendSV中斷,等退出臨界區後,進行上下文切換。入隊任務完成。
上面讨論了最理想的情況,過程也簡潔明了,但如果任務入隊時,隊列滿并且不允許覆寫入隊,則情況會變得複雜起來。
在這種情況下,先看一個簡單分支:阻塞時間為0的情況。設定阻塞時間為0意味着當隊列滿時,函數立即傳回,傳回一個錯誤代碼,表示隊列滿。
如果阻塞時間不為0,則本任務會因為等待入隊而進入阻塞。在将任務設定為阻塞的過程中,是不希望有其它任務和中斷操作這個隊列的事件清單的(隊列結構體成員xTasksWaitingToReceive清單和xTasksWaitingToSend清單),因為操作隊列事件清單可能引起其它任務解除阻塞,這可能會發生優先級翻轉。比如任務A的優先級低于本任務,但是在本任務進入阻塞的過程中,任務A卻因為其它原因解除阻塞了,這顯然是要絕對禁止的。是以FreeRTOS使用挂起排程器來簡單粗暴的禁止其它任務操作隊列,因為挂起排程器意味着任務不能切換并且不準調用可能引起任務切換的API函數。
但挂起排程器并不會禁止中斷,中斷服務函數仍然可以操作隊列事件清單,可能會解除任務阻塞、可能會進行上下文切換,這是不允許的。于是,解決辦法是不但挂起排程器,還要給隊列上鎖!
隊列結構體中有兩個成員跟隊列上鎖有關:xRxLock和xTxLock。這兩個成員變量為queueUNLOCKED(宏,定義為-1)時,表示隊列未上鎖;當這兩個成員變量為queueLOCKED_UNMODIFIED(宏,定義為0)時,表示隊列上鎖。
給隊列上鎖是調用宏prvLockQueue()實作的,代碼很簡單,将隊列結構體成員xRxLock和xTxLock都設定為queueLOCKED_UNMODIFIED。
我們看一下給隊列上鎖是如何起作用的。當中斷服務程式操作隊列并且導緻阻塞的任務解除阻塞時,會首先判斷該隊列是否上鎖,如果沒有上鎖,則解除被阻塞的任務,還會根據需要設定上下文切換請求标志;如果隊列已經上鎖,則不會解除被阻塞的任務,取而代之的是,将xRxLock或xTxLock加1,表示隊列上鎖期間出隊或入隊的數目,也表示有任務可以解除阻塞了。這部分代碼在帶中斷保護的入隊和出隊API函數中,後面我們會講到,這裡先有個印象。
有将隊列上鎖操作,就會有解除隊列鎖操作。函數prvUnlockQueue()用于解除隊列鎖,将可以解除阻塞的任務插入到就緒清單,解除任務的最大數量由xRxLock和xTxLock指定。
經過一系列的邏輯判斷,發現本任務還是要進入阻塞狀态,則調用函數vTaskPlaceOnEventList()來實作。這個函數将揭示任務因等待特定事件而進入阻塞的詳細步驟,其實非常簡單,隻有兩步:第一步,将任務的事件清單項(任務TCB結構體成員xEventListItem)插入到隊列的等待入隊清單(隊列結構體成員xTasksWaitingToSend)中;第二步,将任務的狀态清單項(任務TCB結構體成員xStateListItem)從就緒清單中删除,然後插入到延時清單中,任務的最大延時時間放入xStateListItem. xItemValue中,每次系統節拍定時器中斷服務函數中,都會檢查這個值,檢測任務是否逾時。
當任務成功阻塞在等待入隊操作後,目前任務就沒有必要再占用CPU了,是以接下來解除隊列鎖、恢複排程器、進行任務切換,下一個處于最高優先級的就緒任務就會被運作了。
2.2 xQueueGenericSendFromISR ()
這個函數用于入隊,用于中斷服務程式中。根據參數的不同,可以從隊列尾入隊、從隊列首入隊也可以覆寫式入隊。覆寫式入隊用于隻有一個隊列項的場合,入隊時如果隊列已滿,則将之前的隊列項覆寫掉。函數原型為:
BaseType_t xQueueGenericSendFromISR
(
QueueHandle_t xQueue,
const void * const pvItemToQueue,
BaseType_t * const pxHigherPriorityTaskWoken,
const BaseType_t xCopyPosition
)
xQueue:隊列句柄。
pvItemToQueue:指針,指向要入隊的項目。
pxHigherPriorityTaskWoken:如果入隊導緻一個任務解鎖,并且解鎖的任務優先級高于目前運作的任務,則該函數将*pxHigherPriorityTaskWoken設定成pdTRUE。如果xQueueSendFromISR()設定這個值為pdTRUE,則中斷退出前需要一次上下文切換。從FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken稱為一個可選參數,并可以設定為NULL。
xCopyPosition:入隊位置,可以選擇從隊列尾入隊、從隊列首入隊和覆寫式入隊。
這個函數和xQueueGenericSend()很相似,但是當隊列滿時不會阻塞,直接傳回一個錯誤碼,表示隊列滿(相當于阻塞時間為0)。是以,有了分析xQueueGenericSend()的基礎,這個函數我們很快就能看完。源碼簡化後如下所示:
BaseType_t xQueueGenericSendFromISR(
QueueHandle_t xQueue,
const void * const pvItemToQueue,
BaseType_t * const pxHigherPriorityTaskWoken,
const BaseType_t xCopyPosition )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
traceQUEUE_SEND_FROM_ISR( pxQueue );
/*完成資料拷貝工作,分為從隊列尾入隊,從隊列首入隊和覆寫式入隊*/
( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
/*檢查隊列是否上鎖,如果上鎖,則隊列事件清單不能被改變 */
if( pxQueue->xTxLock == queueUNLOCKED )
{ /*隊列沒有上鎖*/
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* 解除阻塞的任務優先級比目前任務高,記錄上下文切換請求,等傳回中斷服務程式後,可以顯示的強制上下文切換 */
if( pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
}
}
}
else
{
/* 隊列上鎖,增加鎖計數器,等到任務解除隊列鎖時,使用這個計數器就可以知道有多少資料入隊,可以最多解除多少個因等待從隊列讀資料而阻塞的任務 */
++( pxQueue->xTxLock );
}
xReturn = pdPASS;
}
else
{
xReturn = errQUEUE_FULL;
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xReturn;
}
因為沒有阻塞,是以代碼簡單了很多,唯一值得注意的是,當成功入隊後,如果有因為等待出隊而阻塞的任務,現在可以将其中最高優先級的任務解除阻塞,在執行解除阻塞操作之前,會判斷隊列是否上鎖。如果沒有上鎖,則解除被阻塞的任務,還會根據需要設定上下文切換請求标志;如果隊列已經上鎖,則不會解除被阻塞的任務,取而代之的是将xTxLock加1,表示隊列上鎖期間入隊的個數,也表示有任務可以解除阻塞了。
3.出隊
出隊的API函數要相對少一些,也分為帶中斷保護的出隊操作和不帶中斷保護的出隊操作。每種出隊情況都可以選擇是否删除隊列項。出隊API函數如表3-1所示。
表3-1:出隊API接口清單
出隊操作和入隊操作有很多相似性,将入隊流程了解透徹,出隊操作不在話下,是以我們不再分析源碼。
————————————————
版權聲明:本文為CSDN部落客「zhzht19861011」的原創文章,遵循 CC 4.0 BY-SA 版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/zhzht19861011/java/article/details/51510384
歡迎使用Markdown編輯器
你好! 這是你第一次使用 Markdown編輯器 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章,了解一下Markdown的基本文法知識。
新的改變
我們對Markdown編輯器進行了一些功能拓展與文法支援,除了标準的Markdown編輯器功能,我們增加了如下幾點新功能,幫助你用它寫部落格:
- 全新的界面設計 ,将會帶來全新的寫作體驗;
- 在創作中心設定你喜愛的代碼高亮樣式,Markdown 将代碼片顯示選擇的高亮樣式 進行展示;
- 增加了 圖檔拖拽 功能,你可以将本地的圖檔直接拖拽到編輯區域直接展示;
- 全新的 KaTeX數學公式 文法;
- 增加了支援甘特圖的mermaid文法1 功能;
- 增加了 多螢幕編輯 Markdown文章功能;
- 增加了 焦點寫作模式、預覽模式、簡潔寫作模式、左右區域同步滾輪設定 等功能,功能按鈕位于編輯區域與預覽區域中間;
- 增加了 檢查清單 功能。
功能快捷鍵
撤銷:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜體:Ctrl/Command + I
标題:Ctrl/Command + Shift + H
無序清單:Ctrl/Command + Shift + U
有序清單:Ctrl/Command + Shift + O
檢查清單:Ctrl/Command + Shift + C
插入代碼:Ctrl/Command + Shift + K
插傳入連結接:Ctrl/Command + Shift + L
插入圖檔:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替換:Ctrl/Command + G
合理的建立标題,有助于目錄的生成
直接輸入1次#,并按下space後,将生成1級标題。
輸入2次#,并按下space後,将生成2級标題。
以此類推,我們支援6級标題。有助于使用
TOC
文法後生成一個完美的目錄。
如何改變文本的樣式
強調文本 強調文本
加粗文本 加粗文本
标記文本
删除文本
引用文本
H2O is是液體。
210 運算結果是 1024.
插傳入連結接與圖檔
連結: link.
圖檔:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI3p1dw1GT0ljMZpXWU1ENoJDWth3VZlXOW1kdJBDTzgTeOZXUYpVd1kmYr50MZVXSYlFMG1GZolTeMZTTINGMShUYvwlbj5yZtlmbkN3YuQnclZnbvN2Ztl2Lc9CX6MHc0RHaiojIsJye.jpg)
帶尺寸的圖檔:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI3p1dw1GT0ljMZpXWU1ENoJDWth3VZlXOW1kdJBDTzgTeOZXUYpVd1kmYr50MZVXSYlFMG1GZolTeMZTTINGMShUYvwlbj5yZtlmbkN3YuQnclZnbvN2Ztl2Lc9CX6MHc0RHaiojIsJye.jpg)
居中的圖檔:
居中并且帶尺寸的圖檔:
當然,我們為了讓使用者更加便捷,我們增加了圖檔拖拽功能。
如何插入一段漂亮的代碼片
去部落格設定頁面,選擇一款你喜歡的代碼片高亮樣式,下面展示同樣高亮的
代碼片
.
// An highlighted block
var foo = 'bar';
生成一個适合你的清單
- 項目
- 項目
- 項目
- 項目
- 項目1
- 項目2
- 項目3
- 計劃任務
- 完成任務
建立一個表格
一個簡單的表格是這麼建立的:
項目 | Value |
---|---|
電腦 | $1600 |
手機 | $12 |
導管 | $1 |
設定内容居中、居左、居右
使用
:---------:
居中
使用
:----------
居左
使用
----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标點字元轉換為“智能”印刷标點HTML實體。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | | ‘Isn’t this fun?’ |
Quotes | | “Isn’t this fun?” |
Dashes | | – is en-dash, — is em-dash |
建立一個自定義清單
- Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何建立一個注腳
一個具有注腳的文本。2
注釋也是必不可少的
Markdown将文本轉換為 HTML。
KaTeX數學公式
您可以使用渲染LaTeX數學表達式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通過歐拉積分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多關于的資訊 LaTeX 數學表達式here.
新的甘特圖功能,豐富你的文章
- 關于 甘特圖 文法,參考 這兒,
UML 圖表
可以使用UML圖表進行渲染。 Mermaid. 例如下面産生的一個序列圖:
這将産生一個流程圖。:
- 關于 Mermaid 文法,參考 這兒,
FLowchart流程圖
我們依舊會支援flowchart的流程圖:
- 關于 Flowchart流程圖 文法,參考 這兒.
導出與導入
導出
如果你想嘗試使用此編輯器, 你可以在此篇文章任意編輯。當你完成了一篇文章的寫作, 在上方工具欄找到 文章導出 ,生成一個.md檔案或者.html檔案進行本地儲存。
導入
如果你想加載一篇你寫過的.md檔案,在上方工具欄可以選擇導入功能進行對應擴充名的檔案導入,
繼續你的創作。
- mermaid文法說明 ↩︎
- 注腳的解釋 ↩︎