FreeRTOS篇章之二值信號量

寫在前面:
本文章旨在總結備份、方便以後查詢,由於是個人總結,如有不對,歡迎指正;另外,內容大部分來自網絡、書籍、和各類手冊,如若侵權請告知,馬上刪帖致歉。

 

目錄

一、採用二值信號量同步

二、API 函數

三、演示例程


 

一、採用二值信號量同步

二值信號量可以在某個特殊的中斷髮生時,讓任務解除阻塞,相當於讓任務與中斷同步。這樣就可以讓中斷事件處理量大的工作在同步任務中完成,中斷服務例程 (ISR)中只是快速處理少部份工作

如果某個中斷處理要求特別緊急,其延遲處理任務的優先級可以設爲最高,以保證延遲處理任務隨時都搶佔系統中的其它任務

在這種中斷同步的情形下,信號量可以看作是一個深度爲 1的隊列。這個隊列由於最多隻能保存一個數據單元,所以其非空則滿(所謂 “二值” 即二進制,只有 0 /1)。延遲處理任務調用 xSemaphoreTake()時,等效於帶阻塞時間地讀取隊列,如果隊列爲空的話任務則進入阻塞態。當事件發生後,ISR 簡單地通過調用 xSemaphoreGiveFromISR()放置一個令牌(信號量)到隊列中,使得隊列成爲滿狀態。這也使得延遲處理任務切出阻塞態,並移除令牌,使得隊列再次成爲空。

其演示過程:

 

二、API 函數

要想調用以下函數,需要 #include "semphr.h"

1、vSemaphoreCreateBinary() API 函數

void vSemaphoreCreateBinary( xSemaphore )

傳入參數:

  • xSemaphore:創建的信號量

需要說明的是 vSemaphoreCreateBinary()在實現上是一個宏,所以信號量變量應當直接傳入,而不是傳址

注意:在新的版本中一般是通過 xSemaphoreCreateBinary( void )函數返回一個 SemaphoreHandle_t 值,並以此創建一個二值信號量

爲什麼放棄 vSemaphoreCreateBinary()函數以及它們之間的區別我們可以看一下它的註釋:

/* 
  This old vSemaphoreCreateBinary() macro is now deprecated in favour of 
  the xSemaphoreCreateBinary() function. Note that binary semaphores created using 
  the vSemaphoreCreateBinary() macro are created in a state such 
  that the first call to 'take' the semaphore would pass, 
  whereas binary semaphores created using xSemaphoreCreateBinary() are created 
  in a state such that the the semaphore must first be 'given' before it can be 'taken'.
*/

2、xSemaphoreTake() API 函數

BaseType_t   xSemaphoreTake( SemaphoreHandle_t    xSemaphore,
                                                   TickType_t                    xBlockTime );

傳入參數:

  • xSemaphore:獲取得到的信號量。信號量由定義爲 xSemaphoreHandle類型的變量引用;信號量在使用前必須先創建
  • xTicksToWait:阻塞超時時間。任務進入阻塞態以等待信號量有效的最長時間。如果 xTicksToWait 爲0,則 xSemaphoreTake()在信號量無效時會立即返回;如果把 xTicksToWait 設置爲 portMAX_DELAY,那麼阻塞等待將沒有超時限制

返回參數(有兩個可能的返回值):

  • pdTRUE:成功獲得信號量
  • pdFALSE:未能獲得信號量

3、xSemaphoreGive() API 函數

BaseType_t   xSemaphoreGive( SemaphoreHandle_t    xSemaphore );

傳入參數:

  • xSemaphore:給出的信號量。信號量由定義爲 xSemaphoreHandle類型的變量引用;信號量在使用前必須先創建

返回參數(有兩個可能的返回值):

  • pdTRUE:信號量被釋放
  • pdFALSE:信號量已經有效,無法給出

4、xSemaphoreGiveFromISR() API 函數

BaseType_t   xSemaphoreGiveFromISR( SemaphoreHandle_t    xSemaphore,
                                                                BaseType_t                   *pxHigherPriorityTaskWoken );

傳入參數:

  • xSemaphore:給出的信號量。信號量由定義爲 xSemaphoreHandle類型的變量引用;信號量在使用前必須先創建
  • pxHigherPriorityTaskWoken:如果調用 xSemaphoreGiveFromISR()使得一個任務解除阻塞,並且這個任務的優先級高於當前任務(也就是被中斷的任務),那麼 xSemaphoreGiveFromISR()會在函數內部將 *pxHigherPriorityTaskWoken 設爲 pdTRUE。如果xSemaphoreGiveFromISR() 將此值設爲 pdTRUE,則在中斷退出前應當進行一次上下文切換

返回參數(有兩個可能的返回值):

  • pdTRUE:xSemaphoreGiveFromISR()調用成功
  • errQUEUE_FULL:信號量已經有效,無法給出

5、xSemaphoreCreateBinaryStatic() API函數

SemaphoreHandle_t   xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer );

該函數是用於在靜態的時候,利用該函數創建一個二值信號量的,具體可以去看他的註釋,這裏就不說了

 

三、演示例程

二值信號量多用於 1v1這種情況,因爲它只有兩個值(0 /1),非空則滿,有點類似於在非 RTOS中定義的標誌位;由於可以在任務與中斷同步,並且存在超時機制,那本次例程就可以把它應用到串口 DMA接收中

main.c

/*
    FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
    All rights reserved

    VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

    This file is part of the FreeRTOS distribution.

    FreeRTOS is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License (version 2) as published by the
    Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.

    ***************************************************************************
    >>!   NOTE: The modification to the GPL is included to allow you to     !<<
    >>!   distribute a combined work that includes FreeRTOS without being   !<<
    >>!   obliged to provide the source code for proprietary components     !<<
    >>!   outside of the FreeRTOS kernel.                                   !<<
    ***************************************************************************

    FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE.  Full license text is available on the following
    link: http://www.freertos.org/a00114.html

    ***************************************************************************
     *                                                                       *
     *    FreeRTOS provides completely free yet professionally developed,    *
     *    robust, strictly quality controlled, supported, and cross          *
     *    platform software that is more than just the market leader, it     *
     *    is the industry's de facto standard.                               *
     *                                                                       *
     *    Help yourself get started quickly while simultaneously helping     *
     *    to support the FreeRTOS project by purchasing a FreeRTOS           *
     *    tutorial book, reference manual, or both:                          *
     *    http://www.FreeRTOS.org/Documentation                              *
     *                                                                       *
    ***************************************************************************

    http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
    the FAQ page "My application does not run, what could be wrong?".  Have you
    defined configASSERT()?

    http://www.FreeRTOS.org/support - In return for receiving this top quality
    embedded software for free we request you assist our global community by
    participating in the support forum.

    http://www.FreeRTOS.org/training - Investing in training allows your team to
    be as productive as possible as early as possible.  Now you can receive
    FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
    Ltd, and the world's leading authority on the world's leading RTOS.

    http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
    including FreeRTOS+Trace - an indispensable productivity tool, a DOS
    compatible FAT file system, and our tiny thread aware UDP/IP stack.

    http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
    Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.

    http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
    Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
    licenses offer ticketed support, indemnification and commercial middleware.

    http://www.SafeRTOS.com - High Integrity Systems also provide a safety
    engineered and independently SIL3 certified version for use in safety and
    mission critical applications that require provable dependability.

    1 tab == 4 spaces!
*/


/* Standard includes. */
#include <stdio.h>
#include <string.h>

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"        // 信號量相關的頭文件

/* Library includes. */
#include "stm32f10x_it.h"

/* Private app includes. */
#include "bsp_uart.h"
#include "bsp_gpio.h"


/* Task priorities. */
#define mainCREATOR_TASK_PRIORITY           ( tskIDLE_PRIORITY + 1 )

/*----------------------------- End -----------------------------*/

/*
 * User Private Task.
 */
static void prvUser_Task( void *pvParameters );

/*
 * Configure the clocks, GPIO and other peripherals as required by the demo.
 */
static void prvSetupHardware( void );

/*----------------------------- End -----------------------------*/


/************************************************
函數名稱 : main
功    能 : 主函數入口
參    數 : 無
返 回 值 : 無
*************************************************/
int main( void )
{
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();
	
	/* Start the tasks defined within this file/specific to this demo. */
	xTaskCreate( prvUser_Task, "prvUser_Task", configMINIMAL_STACK_SIZE, NULL, mainCREATOR_TASK_PRIORITY, NULL );
	
	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}
/*----------------------------- End -----------------------------*/

/************************************************
函數名稱 : prvSetupHardware
功    能 : 爲了方便管理,所有的用戶任務都放在該函數裏面
參    數 : 無
返 回 值 : 無
*************************************************/
static void prvUser_Task( void *pvParameters )
{	
	/* User-defined private tasks */
	
	printf(">>>>> delete user task\r\n");
	vTaskDelete(NULL);		// 刪除自己
}

/************************************************
函數名稱 : prvSetupHardware
功    能 : 硬件接口初始化配置
參    數 : 無
返 回 值 : 無
*************************************************/
static void prvSetupHardware( void )
{
	/* Start with the clocks in their expected state. */
	RCC_DeInit();

	/* Enable HSE (high speed external clock). */
	RCC_HSEConfig( RCC_HSE_ON );

	/* Wait till HSE is ready. */
	while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
	{
	}

	/* 2 wait states required on the flash. */
	*( ( unsigned long * ) 0x40022000 ) = 0x02;

	/* HCLK = SYSCLK */
	RCC_HCLKConfig( RCC_SYSCLK_Div1 );

	/* PCLK2 = HCLK */
	RCC_PCLK2Config( RCC_HCLK_Div1 );

	/* PCLK1 = HCLK/2 */
	RCC_PCLK1Config( RCC_HCLK_Div2 );

	/* PLLCLK = 8MHz * 9 = 72 MHz. */
	RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );

	/* Enable PLL. */
	RCC_PLLCmd( ENABLE );

	/* Wait till PLL is ready. */
	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
	{
	}

	/* Select PLL as system clock source. */
	RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );

	/* Wait till PLL is used as system clock source. */
	while( RCC_GetSYSCLKSource() != 0x08 )
	{
	}

	/* Configure HCLK clock as SysTick clock source. */
	SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
	
	/*
	 * STM32中斷優先級分組爲 4,即 4bit都用來表示搶佔優先級,範圍爲:0~15
	 * 優先級分組只需要分組一次即可,以後如果有其他的任務需要用到中斷,
	 * 都統一用這個優先級分組,千萬不要再分組,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* Other peripheral configuration */
//	vSetupTimer();
	vSetupUSART();
	vSetupParPort();
}
/*----------------------------- End -----------------------------*/

主程序沒什麼好解釋的,初始化硬件的配置,建立所需的任務;真正實現的任務在下面的源文件中,主要是方便分類,規範一點便於管理

 

bsp_uart.c

#include "bsp_uart.h"

#include <string.h>

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"


#define BAUDRATE_1		115200;			// 波特率設置	支持的波特率:115200,19200,9600,38400,57600,1200,2400,4800
#define BAUDRATE_2		115200;			// 波特率設置	支持的波特率:115200,19200,9600,38400,57600,1200,2400,4800

UartRx_Buff_TypeDef Usart1;

QueueHandle_t xQueueUartTx;
QueueHandle_t xQueueUart1_Rx;

SemaphoreHandle_t xBinarySemaphore = NULL;      // 定義一個二值信號量


static void Uart_DMA_RxConfig( DMA_Channel_TypeDef*DMA_CHx,uint32_t PeripheralBaseAddr,uint32_t MemoryBaseAddr,uint16_t Bufsize,uint32_t Priority,uint32_t Mode );


static void prvUart1_Rx_Task( void *pvParameters )
{	
	for( ; ; )
	{
		xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);    // 無超時阻塞,等待成功獲取信號量;此處也沒有必要檢測返回值
		if(Usart1.RxCounter > 0)
		{
			printf("len=%d 收到數據:%s\n",Usart1.RxCounter,Usart1.RxBuffer);
			memset(Usart1.RxBuffer, 0, RxBUFFER_SIZE);
		}
	}
}

/************************************************
函數名稱 : vSetupUSART
功    能 : UART初始化接口
參    數 : 無
返 回 值 : 無
*************************************************/
void vSetupUSART( void )
{
	UART1_Config();
//	UART2_Config();
	
//	vSemaphoreCreateBinary(xBinarySemaphore);            
	xBinarySemaphore = xSemaphoreCreateBinary();        // 創建二值信號量
	memset(Usart1.RxBuffer, 0, RxBUFFER_SIZE);
	
	xTaskCreate( prvUart1_Rx_Task, "prvUart1_Rx_Task", 500, NULL, tskIDLE_PRIORITY + 3, NULL );
}

/************************************************
函數名稱 : UART1_Config
功    能 : UART1端口配置
參    數 : 無
返 回 值 : 無
*************************************************/
void UART1_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    USART_InitTypeDef USART_InitStructure;

	/* config GPIOA clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
    /* config USART1 clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    /* USART1 GPIO config */
    /* Configure USART1 Tx (PA.09) as alternate function push-pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* Configure USART1 Rx (PA.10) as input floating */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* Enable the USART1 Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* USART1 mode config */
    USART_InitStructure.USART_BaudRate = BAUDRATE_1;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(EVAL_COM1, &USART_InitStructure);

	/* DMA config */
	Uart_DMA_RxConfig(USART1_RX_DMA_CHANNEL,
					(uint32_t)&USART1->DR,
					(uint32_t)Usart1.RxBuffer,
					RxBUFFER_SIZE,
					DMA_Priority_Medium,
					DMA_Mode_Circular);

	USART_ITConfig(EVAL_COM1, USART_IT_IDLE, ENABLE);          // 這裏使能的是空閒中斷
	USART_DMACmd(EVAL_COM1, USART_DMAReq_Rx, ENABLE);
    USART_Cmd(EVAL_COM1, ENABLE);
}

/************************************************
函數名稱 : Uart_DMA_RxConfig
功    能 : 串口 DMA接收配置(限串口 1/2/3)
參    數 : DMA_CHx ---- 
			PeripheralBaseAddr ---- Specifies the peripheral base address for DMAy Channelx
			MemoryBaseAddr ---- Specifies the memory base address for DMAy Channelx
			Bufsize ---- Specifies the buffer size, in data unit, of the specified Channel
			Priority ---- @ref DMA_priority_level
			Mode ---- @ref DMA_circular_normal_mode
返 回 值 : 無
*************************************************/
static void Uart_DMA_RxConfig( DMA_Channel_TypeDef* DMA_CHx,	\
							 uint32_t PeripheralBaseAddr,	\
							 uint32_t MemoryBaseAddr,	\
							 uint16_t Bufsize,	\
							 uint32_t Priority,	\
							 uint32_t Mode )
{
	DMA_InitTypeDef DMA_InitStructure;
	
	/* USARTx RX DMA1 Channel (triggered by USARTx Rx event) Config */
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
    DMA_DeInit(DMA_CHx);
   	DMA_InitStructure.DMA_PeripheralBaseAddr = PeripheralBaseAddr;
	DMA_InitStructure.DMA_MemoryBaseAddr = MemoryBaseAddr;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = Bufsize;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode = Mode;
	DMA_InitStructure.DMA_Priority = Priority;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA_CHx, &DMA_InitStructure);
	
	DMA_ClearFlag(DMA1_FLAG_GL3 | DMA1_FLAG_GL6 | DMA1_FLAG_GL6);
	DMA_ITConfig(DMA_CHx, DMA_IT_TE, ENABLE);
	DMA_Cmd (DMA_CHx,ENABLE);
}

/************************************************
函數名稱 : USART_SendByte
功    能 : 串口字符發送
參    數 : c ---- 發送的數據
返 回 值 : 無
*************************************************/
void USART_SendByte( USART_TypeDef* USARTx, uint8_t c )
{     
	USART_SendData(USARTx, c);
	
	while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}

/************************************************
函數名稱 : USART_SendString
功    能 : 串口字符串發送
參    數 : USARTx ---- 串口
			pData ---- 字符串
			Length ---- 長度
返 回 值 : 無
*************************************************/
void USART_SendString( USART_TypeDef* USARTx, const uint8_t *pData, uint16_t Length )
{
    while(Length--)
    {
        USART_SendByte(USARTx, *pData);
        pData++;
    }
}

/************************************************
函數名稱 : USART_Printf
功    能 : 串口打印輸出
參    數 : USARTx ---- 串口
			String	---- 字符串
返 回 值 : 無
*************************************************/
void USART_Printf( USART_TypeDef* USARTx, char *String )
{
    do
    {
        USART_SendByte(USARTx, *String);
        String++;
    }while((*String) != '\0');
}


/************************************************
函數名稱 : fputc
功    能 : 重定向 c庫函數 printf到 DEBUG_UART
參    數 : ch
返 回 值 : 無
*************************************************/
int fputc(int ch, FILE *f)
{
    /* 發送一個字節數據到 DEBUG_UART */
    USART_SendData(DEBUG_UART, (uint8_t) ch);

    /* 等待發送完畢 */
    while (USART_GetFlagStatus(DEBUG_UART, USART_FLAG_TXE) == RESET);

    return (ch);
}

/************************************************
函數名稱 : fgetc
功    能 : 重定向 c庫函數 scanf到 DEBUG_UART
參    數 : f ---- 文件
返 回 值 : 無
*************************************************/
int fgetc(FILE *f)
{
    /* 等待 DEBUG_UART輸入數據 */
    while (USART_GetFlagStatus(DEBUG_UART, USART_FLAG_RXNE) == RESET);

    return (int)USART_ReceiveData(DEBUG_UART);
}


/************************************************************************/
/*            STM32F10x USART Interrupt Handlers                        */
/************************************************************************/

/**
  * @brief  This function handles USART1 global interrupt request.
  * @param  None
  * @retval None
  */
void USART1_IRQHandler(void)
{
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;


	if(USART_GetITStatus(EVAL_COM1, USART_IT_IDLE)!=RESET)
	{
		DMA_Cmd(USART1_RX_DMA_CHANNEL, DISABLE);
		DMA_ClearFlag(DMA1_FLAG_TC5);
		Usart1.RxCounter = RxBUFFER_SIZE - DMA_GetCurrDataCounter(USART1_RX_DMA_CHANNEL);    // 獲取接收長度
		USART1_RX_DMA_CHANNEL->CNDTR = RxBUFFER_SIZE;
		DMA_Cmd(USART1_RX_DMA_CHANNEL, ENABLE);
				
		xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);    // 'Give' the semaphore to unblock the task
		portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);        // 上下切換
		USART_ReceiveData(EVAL_COM1);			// Clear IDLE interrupt flag bit
	}
	

//  if(USART_GetITStatus(EVAL_COM1, USART_IT_TXE) != RESET)
//  {
//    /* Write one byte to the transmit data register */
//    USART_SendData(EVAL_COM1, TxBuffer[TxCounter++]);

//    if(TxCounter == RxBUFFER_SIZE)
//    {
//      /* Disable the EVAL_COM1 Transmit interrupt */
//      USART_ITConfig(EVAL_COM1, USART_IT_TXE, DISABLE);
//    }
//  }
}


/*---------------------------- END OF FILE ----------------------------*/


這裏跟往常中的配置不一樣,是使用了 DMA + IDLE來進行串口接收處理(),因爲 DMA並不佔用 CPU,DMA只需在傳輸完成的時候產生一箇中斷,告訴 CPU我已經完成了,然後 CPU知道了就去處理數據,這樣子提高了CPU的利用率,可以更好的讓出硬件資源;若是像在 STM32筆記之 USART(串口)中那樣直接用接收中斷來接收,那樣的話,每接收一個數據就要進入中斷,這樣頻繁的進出中斷,打斷程序的執行,只會讓跑起來的程序變得不那麼實時性;而且,既然 ST有這個 DMA資源可以使用,並且是不佔 CPU的,那我們沒必要不使用啊,使用操作系統,無非就是爲了提高實時性,榨乾 CPU的資源,又怎會讓這串口接收而使得效率降低呢?

 

1、串口 DMA接收的流程:

串口 DMA接收在初始化的時候就處於開啓狀態,一直等待數據的到來,在軟件上無需做任何事情,只要在初始化配置的時候設置好配置就可以了

2、數據接收完成後處理:

這裏主要通過串口空閒中斷來判斷接收完成(主要是爲了實現串口 DMA不定長數據的接收),即當串口數據流停止後,就會產生 IDLE中斷

然後我們只需要在中斷中做以下操作:

  • 關閉串口接收DMA通道(防止數據未處理又接收到數據;爲 DMA重新賦值)
  • 清除 DMA所有標誌位
  • 從 DMA寄存器中獲取接收到的數據字節數
  • 重新設置 DMA下次要接收的數據字節數(這裏是給 DMA寄存器重新設置接收的計數值,這個數量只能大於或者等於可能接收的字節數,否則當 DMA接收計數器遞減到 0的時候,就會重載這個計數值,重新循環遞減計數,造成接收緩衝區的數據被覆蓋丟失。)注意:對 DMA的相關寄存器配置寫入,必須要在關閉 DMA的條件進行,否則操作無效
  • 開啓 DMA通道,等待下一次的數據接收

說明一下,STM32的 IDLE的中斷在串口無數據接收的情況下,並不會一直產生的,產生的條件是:當清除 IDLE標誌位後,必須有接收到至少一個數據後,纔開始觸發;當一段時間沒有接收到數據,便產生 IDLE中斷

 

bsp_uart.h

#ifndef __BSP_UART_H
#define __BSP_UART_H


#include <stdio.h>
#include "stm32f10x.h"


#define DEBUG_UART				USART1
#define EVAL_COM1				USART1
#define EVAL_COM2				USART2

#define USART1_RX_DMA_CHANNEL	DMA1_Channel5
#define USART2_RX_DMA_CHANNEL	DMA1_Channel6

#define TxBUFFER_SIZE  	 		100
#define RxBUFFER_SIZE  		 	100

#define UART_QUEUE_RX_LENGTH	2
#define UART_QUEUE_TX_LENGTH	2

typedef struct
{
	uint8_t Receiving_Time;					// 接收時間
	uint8_t Frame_flag;						// 一幀完成標誌
}EVAL_COMx_TypeDef;

typedef struct
{
	uint8_t RxBuffer[RxBUFFER_SIZE];		// 接收暫存緩衝區
	__IO uint8_t RxCounter;					// 接收數據個數
}UartRx_Buff_TypeDef;
extern UartRx_Buff_TypeDef Usart1;

typedef struct
{
	uint8_t TxBuffer[TxBUFFER_SIZE];		// 發送暫存緩衝區
	__IO uint8_t TxCounter;					// 發送數據個數
	USART_TypeDef* COMx;					// 串口號
}UartTx_Buff_TypeDef;

void vSetupUSART( void );
void UART1_Config(void);
void UART2_Config(void);
void USART_SendByte( USART_TypeDef* USARTx, uint8_t c );
void USART_SendString( USART_TypeDef* USARTx, const uint8_t *pData, uint16_t Length );
void USART_Printf( USART_TypeDef* USARTx, char *String );


#endif	/* __BSP_UART_H */


/*---------------------------- END OF FILE ----------------------------*/


 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章