IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> stm32串口发送数据包进行解析,实现人机交互 -> 正文阅读

[嵌入式]stm32串口发送数据包进行解析,实现人机交互

串口收发解析数据包

之前在正点原子的官方历程中我们就可以看到,在串口中断服务函数里面,对接受的数据用一个十六位的数据来判断是否接受完成(即是否在数据包的末尾接收到0x0D,0x0A,他们分别对应的是\r \n),利用高位处理状态,低位则将数据包数量储存下来,

u16 USART_RX_STA

请添加图片描述

那么既然可以判断是否接受到了数据,那么我们是不是可以将数据进行拆分处理,进行储存,然后对程序中的一些变量进行赋值,然后实现人机交互的功能,通过串口;

例如说,我们发送 X90 Y90 Z90那么就会对后面的数据进行接受处理;

在这里我们发送的数据包主要分为2中类型:

HEX数据包:

用于原始数据的接受,比如陀螺仪,以及温度传感器;

请添加图片描述

文本数据包:

常见的使用场景有CNC,以及3D打印使用的G代码;

请添加图片描述

然后在处理的时候,相信你也看到,需要用到状态机的知识;进行逐层判别,

例如正点原子用的是定义一个变量:

u16 USART_RX_STA

当然我们也可以通过将一个变量置0,1,2,3,,等等来表示不同的状态;

数据包为例的话:

请添加图片描述
请添加图片描述

你可能会有一个疑问就是,为什么HEX的包头包尾设置为0xFF与0xFE,这是因为,如果在数据包中存在一样的数值,可能会导致接受混乱的时候,这两个数据是影响比较小的,毕竟接受的数据不可能一直为很大的值;或者说出现为0xFF与0xFE的可能性比较小;通过这样的设置就极大程度的减小了数据包紊乱的问题;

针对文本数据包就比较好表示了,这了用到的是 @ 这个用的不是很多的字符作为包头,然后 \n 回车为包尾;

这里以文本数据包为例,简要讨论一下实现所需要的步骤:

1,串口的中断处理函数,在接受到数据中断以后,根据之前提到的状态机的知识判断数据的有效性;然后储存到char类型的数组中;

2,清空空格这种多余符号,使数据紧凑

3,数据包大体分为两大类:比如说,类似于这个"LED_ON"与"G 91"这么两种数据,处理起来肯定是不一样的;这里他们分为a,b两种类型:

? a:字符串类型的话,调用strcmp(A,B)//if ture return 0把接受数据与事先定义好的字符串进行比较,然后类似于蓝牙的编程方式一样,进入相应的 if 语句,执行相关操作;

? b:CNC,G代码类型的:在数组中寻找特定字母,然后标记位置,最后取出该字母与下一个字母之间的数据;注意一点的是,这些数据是 char 类型的单个数字,需要转化为 int 类型以后乘以每一位相应的位权,得到数据;

字符串类型的数据包,控制LED的开关:

int main(void)
{
	OLED_Init();
	LED_Init();
	Serial_Init();
	
	OLED_ShowString(1, 1, "TxPacket");
	OLED_ShowString(3, 1, "RxPacket");
	
	while (1)
	{
		if (Serial_RxFlag == 1)
		{
			OLED_ShowString(4, 1, "                ");
			OLED_ShowString(4, 1, Serial_RxPacket);
			
			if (strcmp(Serial_RxPacket, "LED_ON") == 0)
			{
				LED1_ON();
				Serial_SendString("LED_ON_OK\r\n");
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2, 1, "LED_ON_OK");
			}
			else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)
			{
				LED1_OFF();
				Serial_SendString("LED_OFF_OK\r\n");
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2, 1, "LED_OFF_OK");
			}
			else
			{
				Serial_SendString("ERROR_COMMAND\r\n");
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2, 1, "ERROR_COMMAND");
			}
			
			Serial_RxFlag = 0;
		}
	}
}

然后这里放几个我写过的函数,适用于G代码,和CNC代码解析;

串口中断处理函数:

void USART1_IRQHandler(void)//êy?Y°üò?@?aí·\0(??3μ)?á?2
{
	static uint8_t RxState = 0;
	static uint8_t pRxPacket = 0;
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		uint8_t RxData = USART_ReceiveData(USART1);
		
		if (RxState == 0)
		{
			if (RxData == '@' && Serial_RxFlag == 0)//??óDó??áè?±ê????á¢?ì??á?μ?oˉêy£???μ?ê?±ü?aá?D?·¢?íμ?ê±oò£?±?′???óD′|àííê?í?aê???ò?′?êy?Yμ?′|àí£?μ????óêü′í?ó
			{                                       //?¤?÷é?ò?′??óêüò??-?úmainoˉêy?D′|àííê±?á?
				RxState = 1;
				pRxPacket = 0;//?2ì???2?±?á?£??ùò???′?μ÷ó?μ?ê±oò£?2?ò??¨?a0
			}
		}
		else if (RxState == 1)
		{
			if (RxData == '\r')
			{
				RxState = 2;
			}
			else
			{
				Serial_RxPacket[pRxPacket] = RxData;
				pRxPacket ++;
			}
		}
		else if (RxState == 2)
		{
			if (RxData == '\n')
			{
				RxState = 0;
				Serial_RxPacket[pRxPacket] = '\0';//×¢òaCó???μ?êy×é?á?2?a\0
				Serial_RxFlag = 1;    //êy?Y?óêüíê3é£??éò???è?mainoˉêy?D
			}
		}
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

清除空格:

void clear_blank(char *a)
{
	int i=0, j=0;
	for (i = 0; i < strlen(a); i++) {
		if (a[i] != ' ') {
			a[j++] = a[i];
		}
		else if(a[i]==' '){
			continue;
		}
	}
	a[j] = '\0';  //?úêy×éμ????2ìí?óò???'\0'//?aà?oí???°μ??D??′|àíoˉêy2?3?í?£?
				  //ò???ê??ó?óê?íêò?oó?ó£?ò???ê?è¥3y??°×?ó
	
}

查找(搜索)特殊字符在数组中的位置:

int search_array( char *a, int n, char x )
{
  int i,x_Tab_dowm;
  int flag=-1;
	
  x_Tab_dowm=x+32;//ò??a?úASCLL′ú???D£?′óD′×???oíD?D′×????à2?32
	
  for(i=0;i<n;i++)
  {
    if(a[i]==x)
	{
		flag=1;
		break;//ò?μ??ì2aμ?×???μ??°£??í2?Dèòa??DDfor?-?·á?£??ùò??aà?ó?breakà′D′;
	}
	
	else if(a[i]==x_Tab_dowm)
	{
		flag=1;
		break;
	}

  }
  if(flag==1)
  	return i;
  else
  	return flag;
}

读取特定字符后面的数据,然后转换成 int 类型:

int get_array_number( char *a, int start , int place)//êy×éa,?eê?????(×???XYZ?ù?úμ?????),Dèòaìáè?êy×?μ?′óD?
{

	int mi = 0, di = 0;
	int temp = 0, result = 0, real_place = 0, i = 0, j = 0;//éù?÷±?á?oóò??¨òa?°ê±3?ê??ˉ±?á?


	for (i = 0; i < place; i++)
	{
		if ( ('0' <= a[start + i + 1]) && (a[start + i + 1] <= '9') ) { real_place++; }//′óDèòa?D??μ?×????aê?£?μ???ò???×????áê?
		if ( ('A' <= a[start + i + 1]) && (a[start + i + 1]<= 'z') ) { break; }//àyè???????Kp,Ki,Kd??DD′|àí£????′??Dèòa?°?òp£?i£?dèy??×???£?μ÷ó?search_arrayoˉêy
		if (a[start + i + 1] == '\0') { break; }

	}
	
	
	for (j = 0; j < real_place; j++)
	{
		mi = pow(10, real_place - j - 1);
		di = (a[start + j + 1] - '0');

		temp = di * mi;
		result = temp+result;
	}
	
	return result;
}

数据处理函数:

int x_place,y_place,z_place;
int x_num,y_num,z_num;

void Data_Handler(void)
{

if (Serial_RxFlag == 1)
		{
	
			clear_blank(Serial_RxPacket);
			
			x_place=search_array(Serial_RxPacket,Serial_RxPacket_Size,'X');
			y_place=search_array(Serial_RxPacket,Serial_RxPacket_Size,'Y');
			z_place=search_array(Serial_RxPacket,Serial_RxPacket_Size,'Z');
			
			x_num = get_array_number(Serial_RxPacket, x_place, Serial_RxPacket_Size);
			y_num = get_array_number(Serial_RxPacket, y_place, Serial_RxPacket_Size);
			z_num = get_array_number(Serial_RxPacket, z_place, Serial_RxPacket_Size);
			
			
			Serial_RxFlag = 0;
		}


}

主函数:

int main(void)
{
	
	OLED_Init();
	LED_Init();
	Serial_Init();
	
//	OLED_ShowString(1, 1, "TxPacket");
	OLED_ShowString(1, 1, "RxPacket");
	
	while (1)
	{
	        Data_Handler();
		    OLED_ShowString(2, 1, "X_num:");
			OLED_ShowString(3, 1, "Y_num:");
			OLED_ShowString(4, 1, "Z_num:");
			
			OLED_ShowNum(2,7,x_num,5);
			OLED_ShowNum(3,7,y_num,5);
			OLED_ShowNum(4,7,z_num,5);
	}
}

Serial.h:

Serial.c:

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:58:04  更:2022-10-08 20:59:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 18:44:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码