Jetson Nano与STM32串口通信实现指南

一、准备器件

        Jetson Nano、STM32F103C8T6、串口。

二、硬件接线

        Jetson Nano的总体引脚图[引用自:零基础入门Jetson Nano——硬件篇]

82bd9c0a2cb547449c28c071d8ee1f63.png

        可以看出我们需要用到8、10引脚。当然Jetson Nano和STM32之间也需要共地,要不然串口传输不了数据。

        STM32端的就不多说了,作者用的是usart1,大家可以根据自己的情况进行更改,除了配置库函数的时候需要改变,其它模式都一样。usart1是A9-TX、A10-RX。

        那么两个的连线就是Jetson上8引脚接A10,10引脚接A9,然后两个板子的GND连起来。

三、重要知识铺垫

format格式:

        我们用opencv对Jetson进行开发的话,我个人比较喜欢发送struct数据包。

def send_data(a):

    tmp=struct.pack("<BBhB",0x2C,0x12,int(a),0x5B)

    com.write(tmp)

        其中的"<BBhB"就是规定了数据包中数据的传输格式,就类似与写C语言时在开头定义int a一样,规定了数据包内数据的类型、字节这些重要数据。

        字节其实是很重要的,他会影响数据的正确性。特别是与STM32的通信,因为他是32位的单片机。我们在写程序时不免用到超过8位的数据,也就是十进制超过255的数据(传送坐标的时候就非常容易超出255)。那么这个时候传送数据的format格式就很重要,我们需要根据需求选择合适的format。比如作者写的数据包,先看帧头帧头,由两个一个字节数据构成,因为只有一个字节,所以就会选择用“B”。相应的,我们在STM32中定义16位的变量接收坐标,所以我们用“h”。

        所以  ,下面这张图还是需要好好看的,特别是一些常用的。f15cf89511c344edbb7dc26627e64062.png

四、代码

Jetson端:

        先附上代码

#coding=utf-8
import cv2
import struct
import serial

com=serial.Serial("/dev/ttyTHS1",115200)

def send_data(a):
    tmp=struct.pack("<BBhB",0x2C,0x12,int(a),0x5B)
    com.write(tmp)
    
while True:
    a=1
    send_data(a)
    keyCode = cv2.waitKey(30) & 0xFF         
    if keyCode == 27:# ESC键退出
        break

cv2.destroyAllWindows()

        第一行“#coding=utf-8”对于不需要进行开机自启的人来说可以不加,需要进行开机自启的如果有中文注释,就需要加,详情请观看博主的另一篇博客Jetson Nano开机启动Python文件。

        这里发送的只有一个数据,大家以此类推,对这个代码进行修改就行了。

        struct库和serial库是必须要导入的,“com=serial.Serial("/dev/ttyTHS1",115200)”这句话就相当于开启了哪个串口,并且设置了多少波特率,这里的波特率需要和STM32串口的波特率一致。

STM32端:

        Jetson端其实看起来就比较简单,所以这次实验的重点就是STM32端串口的配置。

uint16_t cx,cy;
uint8_t RxFlag1 = 0;

void USART1_IRQHandler(void)			 
{
		u8 com_data; 
		u8 i;
		static u8 RxCounter1=0;
		static u16 RxBuffer1[20]={0};
		static u8 RxState = 0;	
		
		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;
				}
		
				else if(RxState==1&&com_data==0x12)  //0x12帧头
				{
					RxState=2;
					RxBuffer1[RxCounter1++]=com_data;
				}
		
				else if(RxState==2)
				{
					RxBuffer1[RxCounter1++]=com_data;

					if(RxCounter1>=20||com_data == 0x5B)       //RxBuffer1接受满了,接收数据结束
					{
						RxState=3;
						RxFlag1=1;
						cx=(RxBuffer1[RxCounter1-4]<<8)+(RxBuffer1[RxCounter1-5]);//RxBuffer1[2]是Jetson发送的第一个数据的低八位,RxBuffer1[3]是Jetson发送的第一个数据的高八位
						cy=(RxBuffer1[RxCounter1-2]<<8)+(RxBuffer1[RxCounter1-3]);//RxBuffer1[4]是Jetson发送的第二个数据的低八位,RxBuffer1[5]是Jetson发送的第二个数据的高八位
						
						OLED_ShowNum(2,1,cx,3);
						OLED_ShowNum(3,1,cy,3);
					}
				}
		
				else if(RxState==3)		//检测是否接受到结束标志
				{
						if(RxBuffer1[RxCounter1-1] == 0x5B)
						{
									USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
									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<20;i++)
						{
								RxBuffer1[i]=0x00;      //将存放数据数组清零
						}
				}

		}
		
}

        串口的配置就不讲了,大家应该都会配置。有些变量时需要定义成全局变量的,比如说接收数据的变量cx、cy。如果要在main函数里运用的话就需要用到extern,而在这里我是直接在中断函数里进行了OLED对接收到的数据的显示,大家自行判断选择。

        重点在于接收的那两句话

cx=(RxBuffer1[RxCounter1-4]<<8)+(RxBuffer1[RxCounter1-5]);//RxBuffer1[2]是Jetson发送的第一个数据的低八位,RxBuffer1[3]是Jetson发送的第一个数据的高八位
                        

cy=(RxBuffer1[RxCounter1-2]<<8)+(RxBuffer1[RxCounter1-3]);//RxBuffer1[4]是Jetson发送的第二个数据的低八位,RxBuffer1[5]是Jetson发送的第二个数据的高八位 

        大家需要理解为什么会有类似与RxCounter1-2这些数字。要想理解这些数字,就得从中断函数接收第一个帧头说起。RxCounter1的初始值是0,当接收到第一个帧头的时候将第一个帧头存到RxBuffer1[0]里,然后再执行RxCounter1++。注意,这里RxCounter1++和++RxCounter1会造成完全不一样的结果,RxCounter1++是先保持RxCounter1的值不动,执行完当前语句后再加一;而++RxCounter1是先加一,再执行当前语句。那么可以分析出当我们接收完帧头的时候,RxCounter1已经是等于2了,因为帧头只有一个字节。当我们接收数据包里的数据时,以作者的代码为例,因为传了两个“h”,也就是两个字节为一个数据,则接收两个数据需要四个字节,这时的RxCounter1等于6.到最后接收帧尾的时候又需要一个字节,所以刚好是7。那么大家对应以上的代码,是不是如注释里所说的一样。

        接收的数据处理方式应该也是比较常规,低八位左移加上高八位。

五、结语

        其实Jetson和STM32的通信也是很常规的,大家多尝试多思考多查资料肯定是能搞定的。

        

       

作者:姬動

物联沃分享整理
物联沃-IOTWORD物联网 » Jetson Nano与STM32串口通信实现指南

发表回复