高速でデータをアップロードするときにSTM32シリアルポートが同時にデータを受信できないという問題を解決します

1.問題の説明

以前のプロジェクトでは、STM32F7シリアルポート3をデータ送信に使用し、ロボットデータを定期的にアップロードしました。上位コンピューター(地上局)がデータを受信して​​インターフェースに表示すると同時に、上位コンピューターが送信できました。パラメータ調整とキャリブレーションの目的を達成するためのロボットへのデータ。データのアップロードは正常で、データの分散は常に少し問題があり、常に機能するとは限りません。しばらくの間パラメータを変更すると、しばらくの間変更できるので、非常に魅力的です。データをダウンロードするためのリンクは、上位のコンピューターのシリアルポートの送信-下位のコンピューターのシリアルポートの受信-下位のコンピューターがパラメーターを変更する-下位のコンピューターが新しいパラメーターをアップロードする-上位のコンピューターが受信する-上位のコンピューターが新しいパラメーターを表示するため、私はいつも混乱しているホストコンピュータ(LabWindowsマルチスレッド)に問題があると思っていました。最近、要約する時間がありましたが、最終的に問題は、STM32シリアルポートがロックされ、にアップロードされたときにデータを受信できないことです。高速

結論から始めましょう:受信できない理由HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)は、シリアルポートがブロックされる原因となるこの関数の使用です。STM32のHALライブラリ関数が本当に私のものである場合があります。

2.解決策

(1)フレーム送信の代わりにバイト送信を使用する

デバッグ後、HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)この関数に問題があることがわかりました。この関数内で何が行われているのかを見てみましょう。

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
    
    
	uint16_t *tmp;
	uint32_t tickstart = 0U;

	/* Check that a Tx process is not already ongoing */
	if (huart->gState == HAL_UART_STATE_READY)
	{
    
    
		if ((pData == NULL) || (Size == 0U))
		{
    
    
			return HAL_ERROR;
		}

		/* Process Locked */
		__HAL_LOCK(huart);

		huart->ErrorCode = HAL_UART_ERROR_NONE;
		huart->gState = HAL_UART_STATE_BUSY_TX;

		/* Init tickstart for timeout managment*/
		tickstart = HAL_GetTick();

		huart->TxXferSize = Size;
		huart->TxXferCount = Size;
		while (huart->TxXferCount > 0U)
		{
    
    
			huart->TxXferCount--;
			if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
			{
    
    
				return HAL_TIMEOUT;
			}
			if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
			{
    
    
				tmp = (uint16_t *)pData;
				huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
				pData += 2;
			}
			else
			{
    
    
				huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
			}
		}
		if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
		{
    
    
			return HAL_TIMEOUT;
		}

		/* At end of Tx process, restore huart->gState to Ready */
		huart->gState = HAL_UART_STATE_READY;

		/* Process Unlocked */
		__HAL_UNLOCK(huart);

		return HAL_OK;
	}
	else
	{
    
    
		return HAL_BUSY;
	}
}

2行のコードが__HAL_LOCK(huart)あり、__HAL_UNLOCK(huart)コメントには、プロセスがロックされ、プロセスがロック解除されていることが示されています。これを見ると、これは強制終了されます。シリアルポートをロックして、受信割り込みを入力できますか?言うのは難しいですが、問題があります。HAL_UART_Transmit一度に大量のデータを送信するため、送信するデータ量が多いと、必然的に正常な受信に失敗します。さて、データのフレームとして送信するのではなく、1バイトとして送信する方法を変更しましょう。

シングルバイト送信機能を備えた以前の標準ライブラリを見てください。

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
    
    
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_DATA(Data)); 
    
  /* Transmit Data */
  USARTx->DR = (Data & (uint16_t)0x01FF);
}

しかし、HALライブラリはなくなりました。それは問題ではありません、あなた自身でそれを書いてください:

int Uart1SendChar(u8 ch)
{
    
    
    while((USART1->ISR&0X40)==0);//循环发送,直到发送完毕   
	USART1->TDR=(u8)ch; 
    return ch;	
}

これは非常に簡単です。つまり、常に送信データレジスタTDRにデータを入力すると、送信されます。

次に、上記の機能を使用して送信するように送信データを変更します。たとえば、使用していることが判明しました

HAL_UART_Transmit(&UART1_Handler, send_buf, 25, 1000);

対応する

for (i=0;i<25;i++)
{
    
    
	Uart1SendChar(send_buf[i]);
}

もう一度テストしてください、ビンゴ、受信は正常です。

(2)DMAを使用して送信する

2番目の方法はDMA、ダイレクトストレージアクセスを使用することです。これには、データ転送がCPUを経由しないという利点があり、CPUリソースを節約し、計算速度を向上させます。シリアルポートDMAに関するチュートリアルはたくさんありますが、非常に厄介な問題の1つは、割り込み関数の処理です。HALライブラリは本当に私を盲目にしました。可変長データの送受信機能はようやく実現しましたが、図書館はそれを理解できず、とても悲しくなりました。後で、HALライブラリコードを完全に乾かすためのシリアル通信/シリアルDMA通信の割り込みメカニズムに関する記事を書く予定なので、ここでは詳しく説明しません。

次に、シリアルポート1のDMAを使用してデータを送信し、DMAを使用して可変長データを受信します。次に、シリアルポート2の通常の方法を使用して、シリアルポート1が受信したデータを出力して監視します。

新しいプロジェクトを作成し、Cubemxでシリアルポート1、シリアルポート2、およびシリアルポート3を開き、同時にDMAを開いて、割り込みを確認します。

ここに画像の説明を挿入
ここに画像の説明を挿入シリアルポート1、2、3の構成は同じです。対応するSreamの選択に注意してください。

デフォルトで生成されるコードには、シリアルポートとDMAのドライバーコードが既に含まれています。直接呼んHAL_UART_Transmit_DMAでください。

シリアルポートアイドル割り込みは可変長データの受信に使用されるため、シリアルポートアイドル割り込みを有効にし、デフォルトのシリアルポート初期化機能を変更する必要があります。

static void MX_USART1_UART_Init(void)
{
    
    

	/* USER CODE BEGIN USART1_Init 0 */

	/* USER CODE END USART1_Init 0 */

	/* USER CODE BEGIN USART1_Init 1 */

	/* USER CODE END USART1_Init 1 */
	huart1.Instance = USART1;
	huart1.Init.BaudRate = 115200;
	huart1.Init.WordLength = UART_WORDLENGTH_8B;
	huart1.Init.StopBits = UART_STOPBITS_1;
	huart1.Init.Parity = UART_PARITY_NONE;
	huart1.Init.Mode = UART_MODE_TX_RX;
	huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart1.Init.OverSampling = UART_OVERSAMPLING_16;
	huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
	huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
	if (HAL_UART_Init(&huart1) != HAL_OK)
	{
    
    
		Error_Handler();
	}
	/* USER CODE BEGIN USART1_Init 2 */
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);		   //使能idle中断
	HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE); //打开DMA接收,数据存入rx_buffer数组中。
	/* USER CODE END USART1_Init 2 */
}

コードの最後の2行が追加されます。これは、IDLE割り込みを開き、シリアルポートDMA転送を開始するためのものです。

次に、割り込み関数を追加します。最初に、cubemxによって生成された割り込みファイル(stm32F7xxx.it.c)に外部データ宣言を追加します。

extern volatile uint8_t rx_len;
extern volatile uint8_t recv_end_flag;
extern uint8_t rx_buffer[100];
extern char BUFFER_SIZE;

使用されるexternは、main.cで定義されているため、main.cの先頭に定義を追加します。

volatile uint8_t rx_len;
volatile uint8_t recv_end_flag;
uint8_t rx_buffer[100];
char BUFFER_SIZE;

割り込み関数はその中に直接記述されUSART1_IRQHandler(void)ており、コールバック関数は地獄です。

void USART1_IRQHandler(void)
{
    
    
	/* USER CODE BEGIN USART1_IRQn 0 */

	/* USER CODE END USART1_IRQn 0 */
	HAL_UART_IRQHandler(&huart1);
	/* USER CODE BEGIN USART1_IRQn 1 */

	uint32_t tmp_flag = 0;
	uint32_t temp;
	tmp_flag = __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE);    //获取IDLE标志位
	if ((tmp_flag != RESET))      //idle标志被置位
	{
    
    
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
	    //清除标志位
		temp = huart1.Instance->ISR; 			//清除状态寄存器SR(F0的HAL库USART_TypeDef结构体中名字为ISR:USART Interrupt and status register),读取SR可以清楚该寄存器
		temp = huart1.Instance->RDR; 			//读取数据寄存器中的数据,读取DR(F0中为RDR:USART Receive Data register)
		HAL_UART_DMAStop(&huart1);
		temp = hdma_usart1_rx.Instance->NDTR;   //获取DMA中未传输的数据个数,NDTR寄存器分析见下面
		rx_len = BUFFER_SIZE - temp;       		//总计数减去未传输的数据个数,得到已经接收的数据个数
		recv_end_flag = 1;                 		//接受完成标志位置1
	}
	/* USER CODE END USART1_IRQn 1 */
}

割り込み機能では、まずIDLEアイドル割り込みフラグビットを取得し、次にフラグビットをクリアし、シリアルポートDMA送信を停止し、受信データ数を取得し、受信完了フラグを設定し、受信データ処理をメイン機能に配置します。 。

main.cにシリアルポート1とシリアルポート2の送信バッファを追加します。

uint8_t i = 0;
uint8_t count = 0;
uint8_t send_buf[25] = {
    
    0};
uint8_t send[25] = {
    
    0};
send_buf[0] = 0xAA;
send_buf[1] = 0xAA;
for (i = 2; i < 25; i++)
	send_buf[i] = 0x50;

次に、while(1)ループを変更します

while (1)
{
    
    
	/* USER CODE END WHILE */
	HAL_UART_Transmit_DMA(&huart1, send_buf, 25); //开启DMA传输
												  //	HAL_UART_Transmit(&huart1,send_buf,25,1000);//开启DMA传输
	if (recv_end_flag == 1)
	{
    
    
		//printf("rx_len=%d\r\n", rx_len);					//打印接收长度
		count++;
		send[0] = count; 
		HAL_UART_Transmit(&huart2, send, 1, 200);			//接收数据打印出来,标记接收数据的数量
		HAL_UART_Transmit(&huart2, rx_buffer, rx_len, 200); //接收数据打印出来
		HAL_UART_Transmit(&huart2, &send[1], 2, 200); //接收数据打印出来
		for (uint8_t i = 0; i < rx_len; i++)
		{
    
    
			rx_buffer[i] = 0; //清接收缓存
		}
		rx_len = 0;											   //清除计数
		recv_end_flag = 0;									   //清除接收结束标志位
		HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE); //重新打开DMA接收
	}

	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);
	HAL_Delay(20);
	/* USER CODE BEGIN 3 */
}

whileループでは、データはシリアルポート1 DMAを介して定期的にアップロードされ、シリアルポート1で受信されたデータはシリアルポート2を介して出力されます。コンピューターで2つのシリアルポートデバッグアシスタントを開き、2つのシリアルポートをそれぞれ接続して、シリアルポート1とシリアルポート2から返送されたデータを確認します。図に示すように、左側はシリアルポート1から返送されたデータです。[送信]をクリックしてデータをボードに送信します。右側のデータは、シリアルポート2から返送されたデータです。左側の[送信]をクリックすると、左側に送信されたデータが右側に表示されます。

正しいデータのフレームと間違ったデータのフレームがあるたびに、私は再び魅了され、魅了されていることに気づきました。デバッグ。

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_30267617/article/details/115276566