当前位置:   article > 正文

2024年超详细OpenMV与STM32单片机通信 (有完整版源码),2024年最新物联网嵌入式开发校招面试经验汇总_openmv与单片机串口通信

openmv与单片机串口通信

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;          		 //PA9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  		 //设定IO口的输出速度为50MHz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	   		 //复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);             	 	 //初始化PA9
	//USART1_RX	  PA10
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;             //PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);                 //初始化PA10 

	//USART1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC\_InitStructure.NVIC\_IRQChannelPreemptionPriority=0 ;  //抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		    //子优先级2
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			      					                 //IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	                          //根据指定的参数初始化VIC寄存器

	//USART 初始化设置
	USART_InitStructure.USART_BaudRate = 115200;                  //串口波特率为115200
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;   //字长为8位数据格式
	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(USART1, &USART_InitStructure);                     //初始化串口1

	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能中断
	USART_Cmd(USART1, ENABLE);                     //使能串口1
  	USART_ClearFlag(USART1, USART_FLAG_TC);        //清串口1发送标志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

}

//USART1 全局中断服务函数
void USART1_IRQHandler(void)
{
u8 com_data;
u8 i;
static u8 RxCounter1=0;
static u16 RxBuffer1[10]={0};
static u8 RxState = 0;
static u8 RxFlag1 = 0;

	if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)  	   //接收中断  
	{
			USART_ClearITPendingBit(USART1,USART_IT_RXNE);   //清除中断标志
			com_data = USART_ReceiveData(USART1);
		
			if(RxState==0&&com\_data==0x2C)  //0x2c帧头
			{
				RxState=1;
				RxBuffer1[RxCounter1++]=com_data;OLED_Refresh();
			}
	
			else if(RxState==1&&com\_data==0x12)  //0x12帧头
			{
				RxState=2;
				RxBuffer1[RxCounter1++]=com_data;
			}
	
			else if(RxState==2)
			{
				RxBuffer1[RxCounter1++]=com_data;

				if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
				{
					RxState=3;
					RxFlag1=1;
					Cx=RxBuffer1[RxCounter1-5];
					Cy=RxBuffer1[RxCounter1-4];
					Cw=RxBuffer1[RxCounter1-3];
					Ch=RxBuffer1[RxCounter1-2];

				}
			}
	
			else if(RxState==3)		//检测是否接受到结束标志
			{
					if(RxBuffer1[RxCounter1-1] == 0x5B)
					{
								USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
								if(RxFlag1)
								{
								OLED_Refresh();
								OLED_ShowNum(0, 0,Cx,3,16,1);
								OLED_ShowNum(0,17,Cy,3,16,1);
								OLED_ShowNum(0,33,Cw,3,16,1);
								OLED_ShowNum(0,49,Ch,3,16,1);
								}
								RxFlag1 = 0;
								RxCounter1 = 0;
								RxState = 0;
								USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
					}
					else   //接收错误
					{
								RxState = 0;
								RxCounter1=0;
								for(i=0;i<10;i++)
								{
										RxBuffer1[i]=0x00;      //将存放数据数组清零
								}
					}
			} 

			else   //接收异常
			{
					RxState = 0;
					RxCounter1=0;
					for(i=0;i<10;i++)
					{
							RxBuffer1[i]=0x00;      //将存放数据数组清零
					}
			}

	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

}


解释:OpenMV发送数据包给STM32,STM32利用中断接收数据并把数据存放在RxBuffer1这个数组里,并且在中断中利用OLED显示cx,cy,cw,ch四个坐标。在中断中,有如下函数:



  • 1
  • 2
  • 3
  • 4
  • 5

else if(RxState==2)
{
RxBuffer1[RxCounter1++]=com_data;

				if(RxCounter1>=10||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
				{
					RxState=3;
					RxFlag1=1;
					Cx=RxBuffer1[RxCounter-5];
					Cy=RxBuffer1[RxCounter-4];
					Cw=RxBuffer1[RxCounter-3];
					Ch=RxBuffer1[RxCounter1-2];

				}
			}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

RxBuffer1是一个装有接收OpenMV数据的数组,RxCounter1起着一个计数器的作用,当RxBuffer[RxCounter1-1]存放的数据为数据包的帧位时,说明已经接收成功整个数据包,此时RxBuffer[RxCounter1-2]存放ch坐标值,RxBuffer[RxCounter1-3]存放cw坐标值,RxBuffer[RxCounter1-4]存放cy坐标值,RxBuffer[RxCounter1-5]存放cx坐标值,此后在RxState=3过程中将这四个坐标显示出来即可。  
 特别注意的是:STM32中断每发生一次,只会接收到一字节的数据,因此,进行七次才会接收完一整帧的数据包,这一点需要读者仔细揣摩,结合上文中说的静态变量关键字static,定义了:



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

u8 com_data;
u8 i;
static u8 RxCounter1=0;
static u8 RxBuffer1[10]={0};
static u8 RxState = 0;
static u8 RxFlag1 = 0;


**请读者仔细揣摩为什么com\_data(端口接收到的数据)、i定义的是动态的(auto),而RxBuffer1(装接收到数据的静态全局数组)、RxState(状态标志变量)、RxFlag1(接受结束标志变量)定义的确实静态的,这一点并不难理解。**




---


## 5.利用PC端测试数据数据是否发送接收正常


在进行OpenMV与STM32的通信测试过程中,我使用了USB转TTL模块,将OpenMV(或STM32单片机)与PC端进行通信确保数据发出或者接收正常。  
 **OpenMV&&PC**  
 OpenMV\_RX接模块TX  
 OpenMV\_TX接模块RX  
 OpenMV\_GND接模块GND  
 然后打开OpenMV,在大循环while(True)中使用语句:



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

DATA=bytearray[(1,2,3,4,5)]
uart.write(DATA)


打开PC端串口助手,注意设置一样的波特率、停止位、发送字节数等,查看串口助手是否接受到了数据。  
 **STM32&&PC**  
 STM32\_RX接模块TX  
 STM32\_TX接模块RX  
 STM32\_GND接模块GND  
 **注意:不管是STM32与PC还是OpenMV与PC还是STM32与OpenMV通信,都要将二者的GND连接在一起。**  
 在main.c中先调用stdio头文件,大循环中使用如下语句:



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

while(1)
{
printf(“HelloWorld!”);
}


打开串口助手查看是否接收到了数据。




---


## 6.学习补充 (代码看不懂的时候可以来看一下)



> 
> **补充1:static关键字(静态变量)的使用**
> 
> 
> 


static 修饰全局函数和全局变量,只能在本源文件使用。举个例子,比如用以下语句`static u8 RxBuffer[10]` 定义了一个名为RxBuffer的静态数组,数组元素类型为unsigned char型。在包含Rxbuffer的源文件中,Rxbuffer相当于一个全局变量,任意地方修改RxBuffer的值,RxBuffer都会随之改变。而且**包含RxBuffer的函数在多次运行后RxBuffer的值会一直保存(除非重新赋值)**。在C语言学习中,利用static关键字求阶乘是一个很好的例子:



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

#include“stdio.h”
long fun(int n);
void main()
{
int i,n;
printf(“input the value of n:”);
scanf(“%d”,&n);
for(i=1;i<=n;i++)
{
printf(“%d! = %1d\n”,i,fun(i));
}
}

long fun(int n)
{
static long p=1;
p=p*n;
return p;
}


效果为依次输出n!(n=1,2,3…n)  
 这个例子中,第一次p的值为1,第二次p的值变成了p x n=1 x 2=2,这个值会一直保存,如果p没有定义为静态类型,那么在第一次运算过后p的值会重新被赋值为1,这就是auto型(不声明默认为auto型)与static型的最大区别。



> 
> 总结:static关键字定义的变量是全局变量,在static所包含的函数多次运行时,该变量不会被多次初始化,只会初始化一次。
> 
> 
> 



> 
> **补充2:extern关键字(外部变量)的使用**
> 
> 
> 


程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量则称为外部变量,外部变量也就是我们所讲的全局变量。它的存储方式为静态存储,其生存周期为整个程序的生存周期。**全局变量可以为本文件中的其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。**  
 如果整个工程由多个源文件组成,在一个源文件中想引用另外一个源文件中已经定义的外部变量,同样只需在引用变量的文件中用 extern 关键字加以声明即可。下面就来看一个多文件的示例:



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

/****max.c****/
#include <stdio.h>
/*外部变量声明*/
extern int g_X ;
extern int g_Y ;
int max()
{
return (g_X > g_Y ? g_X : g_Y);
}
/***main.c****/
#include <stdio.h>
/*定义两个全局变量*/
int g_X=10;
int g_Y=20;
int max();
int main(void)
{
int result;
result = max();
printf(“the max value is %d\n”,result);
return 0;
}
运行结果为:
the max value is 20


对于多个文件的工程,都可以采用上面这种方法来操作。对于模块化的程序文件,可在其文件中预先留好外部变量的接口,也就是只采用 extern 声明变量,而不定义变量,max.c 文件中的 g\_X 与 g\_Y 就是如此操作的。比如想要在主函数中调用usart.c中的变量x,usart.c中有着这样的定义:`static u8 x=0`在usart.h中可以这样写:`extern u8 x`在main.c中包含usart.h头文件,这样在编译的时候就会在main.c中调用x外部变量。



> 
> 总结:extern关键字是外部变量,静态类型的全局变量,可以在源文件中调用其他文件中的变量,在多文件工程中配合头文件使用。
> 
> 
> 



> 
> **补充3:MicroPython一些库函数的解释**
> 
> 
> 


1.ustruct.pack函数:  
 import ustruct,在ustruct中



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

data = ustruct.pack(“<bbhhhhb”, #格式为俩个字符俩个短整型(2字节)
0x2C, #帧头1
0x12, #帧头2
int(cx), # up sample by 4 #数据1
int(cy), # up sample by 4 #数据2
int(cw), # up sample by 4 #数据1
int(ch), # up sample by 4 #数据2
0x5B)


""bbhhhhb"简单来说就是要发送数据的声明,bbhhhhb共七个,代表发送七个数据,对照下面的表,可以知道七个数据按时序发送为unsigner char、unsigned char、short、short、short、short、unsigned char。0x2c为数据帧的帧头,即检测到数据流的开始,但是一个帧头可能会出现偶然性,因此设置两个帧头0x2c与0x12以便在中断中检测是否检测到了帧头以便存放有用数据。0x5b为帧尾,即数据帧结束的标志。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210504040156260.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpaGFvdGlhbjExMQ==,size_16,color_FFFFFF,t_70#pic_center)  
 2.bytearray([ , , , ])函数:  
 用于把十六进制数据以字节形式存放到字节数组中,以便以数据帧的形式发送出去进行通信。



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])
uart,write(FH)




---


## 7.效果展示(可以先来看效果)


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210504041702364.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpaGFvdGlhbjExMQ==,size_16,color_FFFFFF,t_70#pic_center) 从上到下依次为CX,CY,CW,CH




---


## 8.博客更新


1.有朋友反馈OpenMv端找不到色块就会报错,解决方案如下:



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

while(True):
clock.tick()
img = sensor.snapshot()
blobs = img.find_blobs([red_threshold_01])
cx=0;cy=0;
if blobs:
max_b = find_max(blobs)
#如果找到了目标颜色
cx=max_b[5]
cy=max_b[6]
cw=max_b[2]
ch=max_b[3]
img.draw_rectangle(max_b[0:4]) # rect
img.draw_cross(max_b[5], max_b[6]) # cx, cy
FH = bytearray([0x2C,0x12,cx,cy,cw,ch,0x5B])
#sending_data(cx,cy,cw,ch)
uart.write(FH)
print(cx,cy,cw,ch)


在以上代码中,将**max\_b = find\_max(blobs) 移到if blobs外即可**。




---


2.有朋友反馈OpenMV发送数据只能发送一个字节,也就是说大于255的数据无法直接通过代码完成,现在提供以下解决方案:在STM32端代码中依次保存大于255数字的高八位和低八位最后在组合在一起即可。  
 2021/9/15更新 4字节与浮点数之间的转换(参考)




**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/85046572a280c347880b052a47652ed6.png)
![img](https://img-blog.csdnimg.cn/img_convert/7dc6fe95ed35cf9e7e8bf2e16a185dea.png)

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

无法直接通过代码完成,现在提供以下解决方案:在STM32端代码中依次保存大于255数字的高八位和低八位最后在组合在一起即可。  
 2021/9/15更新 4字节与浮点数之间的转换(参考)




**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
[外链图片转存中...(img-xbY0U8FD-1715706990589)]
[外链图片转存中...(img-dLVkbGJN-1715706990590)]

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/682099
推荐阅读
相关标签
  

闽ICP备14008679号