詳細なシリアル通信(STM32F103ZET6に基づくプロジェクトレベルの送受信メカニズム)

はじめ
のように、物事の労働者部門(5つのトップのIT業界の1)は、シリアル通信は、シリアル通信を十分に把握することができ、間違いなく重要なポイントの一つである、それは不可欠です!読者がシリアルポートをよりよく理解して学ぶことができるように、私はプロジェクトのアプリケーションシリアルポートをほとんどの学者が学んだSTM32F103ZET6に移植し、検証に合格しました!

目的

外部MCUとの通信を実現し、複数のデータバッファ機能を含むデータの受信と処理を完了し、シリアルポートのパフォーマンスを向上させます!データの解析に成功すると、外部MCUへのデータ送信が完了し、通信処理が完了します。

概念

プロジェクトで理解しなければならないのは、スレッドとプロセスの関係です:(簡単に説明してください)

  1. プロセスは割り当ての基本単位であり、プログラムの実行のインスタンスであり、プログラムの実行時に確立されます。
  2. スレッドは、プログラムを実行するための最小単位であり、プロセスの実行フローであり、プロセスは複数のスレッドで構成できます。
    STM32開発ボードのメイン関数でwhile(1)が無限ループを作成することと簡単に理解できます。つまり、whileループに対応するスレッドとして簡単に理解できるスレッドであり、ループはそうではない場合があります。プロジェクトの内容に応じて終了しました。

初期化

タイマーを初期化し、10msでオーバーフロー時間を決定し、シリアルポートを初期化し、通信するシリアルポートを選択します

プログラムの説明

ヘッダーファイルの呼び出し:ヘッダーファイルの
コメントを見てください。string.hは非常に重要です。後続のプログラムで使用されるコピー関数memcpy()と値関数memset()がすべて使用されます。特定のパラメーターについては、ライブラリ関数を確認できます。

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "stdio.h"
#include "string.h"//字符串操作头文件

マクロ定義の
説明はウォッチングを参照

#define len 10//根据自己情况调节每次接收数据的最大长度
#define ARRAY_NUM 4//缓存从外部mcu收到的数据条数
#define ture 1
#define fals 0
typedef struct _usart//每个数据的参数,数据,标志位信息
{
    
    
	uint16_t DATA_LEN;
	uint8_t DATA[250];
	uint8_t FLAG;
}usart_;

変数の定義

uint8_t bufff[len];//保存长度为len的字节
uint16_t USART_RX_STA=0;
usart_ g_uart[ARRAY_NUM];//保存数据为ARRAY_NUM的数据

フォーカス

エディターは以前にも多くの方法を試し、最終的にこの一連の処理方法を選択しました:
シリアルポートの初期化とデータの受信
プロセスコーミング:
バイトが受信されるたびに、割り込みがトリガーされます(割り込みをトリガーするためのストップビット)受信したデータは、当社が定義したグローバルバフに直接格納されます。現時点では、受信したデータの長さに注意する必要があります。長さが長さを超えたら、他のデータを破棄することを選択します。

void 中断函数入口(void)
{
    
    
	if(判断是否为接收中断触发)
	{
    
    
		uint8_t 临时变量;
		临时变量=(uint8_t)接收函数(串口);
		if(接收的数据长度>=预定的长度)
		{
    
    
			接收的数据长度=预定的长度; 
			返回;
		}
		串口数据赋值给定义的数组;
	}
}
void usart_init(u32 bound)
{
    
    
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	USART_InitStructure.USART_BaudRate=bound;
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStructure.USART_Parity=USART_Parity_No;
	USART_InitStructure.USART_StopBits=USART_StopBits_1;
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;
	USART_Init(USART1,&USART_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	USART_Cmd(USART1, ENABLE);
	
}

void USART1_IRQHandler(void)
{
    
    
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
    
    
		uint8_t res;
		res=(uint8_t)USART_ReceiveData(USART1);
		if(USART_RX_STA>=len)
		{
    
    
			USART_RX_STA=len; 
			return;
		}
		bufff[USART_RX_STA++]=res;
	}
	else if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
	{
    
    
		;
	}
}

タイマー割り込みの初期化とデータ処理初期化
の焦点はオーバーフロー時間である10msです。2つのデータ送信間の時間間隔が10ms未満の場合、1フレームのデータと判断されます。
計算式:
Tout =(reload value + 1)*(周波数値+1)/ 72MHz
プロセス概要
常にタイマーを開始し、サイクルカウントし、シリアルデータバッファ量の初期値とタイマーに保存されるデータ数はすべてゼロです。タイマーの期限が切れたら、最初に比較するグローバル変数numシリアルポートUSART_RX_STAで受信したデータの数。シリアルポートがこのオーバーフロー期間中に新しいデータを受信すると、タイマー割り込みに入ります。このとき、初期値はnum = 0ですが、USART_RX_STA = 1、タイマー割り込みで判断されます。USART_RX_STA> numの場合、データのフレームが完全に受信されていないことを意味します。等しく、ゼロでない場合は、最後のオーバーフローで新しいデータが受信されていないことを意味します。時間(10ms)。データのフレームの受信が終了したと判断され、このデータを保存する構造の配列が空の場合は循環し、空の場合はデータをこの配列にコピーします。開いた配列スペースが占有されている場合、このデータを無視する1
文の要約:
構造内の配列メモリが空であるかどうかを判断するサイクル。空であることが検出されると、受信したデータがこの構造アルゴリズムのコード分析にコピーされ
ます。

void 定时器中断函数入口(void) {
    
      	
if(判断是否为定时器中断) 	
{
    
    	   	static uint16_t num定义静态变量;
 		static uint8_t index定义静态变量;
 		unsigned char buff_busy定义局部变量; 		
 		if(串口中断接收数据数量>静态变量) 
 		{
    
     		
 		   静态变量=串口中断接收数据数量; 		
 		} 		
 		else if(静态变量==串口中断接收数据数量&&静态变量!=0) 		
 		{
    
    
			  while(g_uart[静态变量%定义的缓存数组数量].FLAG==ture)
				{
    
    
					静态变量=(静态变量+1)%定义的缓存数组数量;
					if(局部变量++>定义的缓存数组数量)
					{
    
    
						将收到的数据清空;
						USART_RX_STA=0;//清零
						num=0;
						break;
					}
					
				}
				一旦检测到空数组,就开始赋值;
	            g_uart[idex].DATA_LEN=num;//数组长度
				memset(g_uart[idex].DATA,0,g_uart[idex].DATA_LEN);	//将要赋值的结构体数组清零		memcpy(g_uart[idex].DATA,bufff,g_uart[idex].DATA_LEN);//将要赋值的数据存到清零的结构体
	g_uart[idex].FLAG=ture;//将此结构体的标志位标志位ture
memset(bufff,0,g_uart[idex].DATA_LEN);//将串口接收的数据bufff成功赋值给结构体后立即清零,方便下一次的接收
					USART_RX_STA=0;//方便下一次数据接收计数
					num=0; 	//清零	
					} 		
					else  		
					{
    
    
					 	; 		
					} 	
		  LED1=!LED1;
		  TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除中断标志位
		  	}
	      }
void timer_init(void)
{
    
    
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	LED_Init();
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period= 100;
	TIM_TimeBaseInitStructure.TIM_Prescaler=7199;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	TIM_Cmd(TIM3, ENABLE);
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE );
	NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
	NVIC_Init(&NVIC_InitStructure);
	
}
void TIM3_IRQHandler(void)
{
    
     
	if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
	{
    
    	  
		static uint16_t num=0;
		static uint8_t idex=0;
		unsigned char buff_busy;
		if(USART_RX_STA>num)
		{
    
    
			num=USART_RX_STA;
		}
		else if(USART_RX_STA==num&&num!=0)
		{
    
    
			  while(g_uart[idex%ARRAY_NUM].FLAG==ture)
				{
    
    
					idex=(idex+1)%ARRAY_NUM;
					if(buff_busy++>ARRAY_NUM)
					{
    
    
						memset(bufff,0,g_uart[0].DATA_LEN);
						USART_RX_STA=0;
						num=0;
						break;
					}
					
				}
				  g_uart[idex].DATA_LEN=num;
					memset(g_uart[idex].DATA,0,g_uart[idex].DATA_LEN);
					memcpy(g_uart[idex].DATA,bufff,g_uart[idex].DATA_LEN);
					g_uart[idex].FLAG=ture;
					
					memset(bufff,0,g_uart[idex].DATA_LEN);
					USART_RX_STA=0;
					num=0;
		}
		else 
		{
    
    
			;
		}
		
		
		  LED1=!LED1;
		  TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
	}
	   
	
}

総括する:

シリアルポートは(特定の数内の)データの受信のみを担当し、タイマーはデータの送受信を監視すると同時に、データのコピーと配列への保存を担当します。whileループは常に担当します。アレイ内のデータを検出して分析し、それに応じて応答します。自分たちでデータを処理することなく、シリアルポート送信機能を直接呼び出すことができます。
読者同士のコミュニケーションやコミュニケーションがたくさんできれば幸いです。ありがとうございます!

おすすめ

転載: blog.csdn.net/weixin_42271802/article/details/105273861