天天看點

Openmv與tm4c通信筆記Openmv與tm4c通信筆記:

Openmv與tm4c通信筆記:

文章目錄

  • Openmv與tm4c通信筆記:
    • 1.Uart等初始化及配置
    • 2.控制Openmv執行指令
    • 3.控制輸入

首先,用個例子來簡單理理通信的步驟:

​ 假如我現在想要識别紅杆,先要在主函數裡寫入對應的發送模式(這裡假設 寫入Mode = 0x12為識别紅杆),然後tm4c會根據你寫入的發送函數,将Mode = 0x12的資料包發送給Openmv執行,在Openmv裡得到你識别紅杆需要的資料,然後再在Openmv中選擇對應的發送模式,将我們需要的資料打包後以包的形式發送過來(這裡要注意,根據所需資料的不同,包的形式也不同,具體看個人對包怎麼建立),然後tm4c再一個一個将資料拿出,用switch的方式選擇接收的資料存到哪些變量中,再把變量調用回主函數。

注意:資料包的發送和接收是按我們規定的時間間隔執行的。

1.Uart等初始化及配置

#include "Basic.h"
#include "drv_Uart2.h"
#include "Quaternion.h"
#include "MeasurementSystem.h"
#include "STS.h"
#include "Sensors_Backend.h"
#include "RingBuf.h"
#include "TM4C123GH6PM.h"
#include "uart.h"
#include "sysctl.h"
#include "gpio.h"
#include "pin_map.h"
#include "interrupt.h"
#include "hw_ints.h"
#include "hw_gpio.h"
#include "Timer.h"
#include "udma.h"
#include "drv_LED.h"
#include "InteractiveInterface.h"
/*引入各頭檔案*/
#define Uart0_BUFFER_SIZE 64  /*緩存空間大小*/
//序列槽中斷
static void UART0_Handler();

/*發送緩沖區*/
	static uint8_t Uart0_tx_buffer[Uart0_BUFFER_SIZE]; //64位發送資料緩存區
	static RingBuf_uint8_t Uart0_Tx_RingBuf; //定義環形緩存輸出
/*發送緩沖區*/

/*接收緩沖區*/
	static uint8_t Uart0_rx_buffer[Uart0_BUFFER_SIZE]; //64位發送資料接收區
	static RingBuf_uint8_t Uart0_Rx_RingBuf;//定義環形緩存接收
/*接收緩沖區*/

static bool SDI_RCTrigger( unsigned int Task_ID );  //聲明觸發函數及接收、發送主函數
static void SDI_Server( unsigned int Task_ID );

static bool SDI_TXTrigger( unsigned int Task_ID );
static void SDI_Tx_model_contrl( unsigned int Task_ID );

void init_drv_Uart0()
{
	//使能Uart0引腳(Rx:PA0 Tx:PA1)
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
	//使能UART0
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	
	//配置GPIO
	GPIOPinConfigure(GPIO_PA0_U0RX);
	GPIOPinConfigure(GPIO_PA1_U0TX);
	GPIOPinTypeUART(GPIOA_BASE, GPIO_PIN_0 | GPIO_PIN_1);//GPIO的UART模式配置
		
	//配置Uart
	UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet() , 115200,			
	                   (UART_CONFIG_WLEN_8 
										| UART_CONFIG_STOP_ONE 
										|	UART_CONFIG_PAR_NONE));	
	
	//初始化緩沖區
	RingBuf_uint8_t_init( &Uart0_Tx_RingBuf , Uart0_tx_buffer , Uart0_BUFFER_SIZE );
	RingBuf_uint8_t_init( &Uart0_Rx_RingBuf , Uart0_rx_buffer , Uart0_BUFFER_SIZE );
	
	//配置序列槽接收中斷
	UARTIntEnable( UART0_BASE , UART_INT_RX | UART_INT_RT);
	UARTIntRegister( UART0_BASE , UART0_Handler );
	
	//配置DMA發送                通道             
	uDMAChannelControlSet( UDMA_PRI_SELECT | UDMA_CH9_UART0TX , \
	UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1 );
    //8位位元組     發送位址(源位址)按8位增加   接收位址(目标位址)按0位增加   總線使用帶寬1。
	UARTDMAEnable( UART0_BASE , UART_DMA_TX ); //使能Tx
	UARTIntRegister( UART0_BASE , UART0_Handler );	 //注冊中斷
	uDMAChannelAssign(UDMA_CH9_UART0TX  );	 //配置設定通道9給Tx
	
	//打開中斷
	IntPrioritySet( INT_UART0 , INT_PRIO_7 );
	IntEnable( INT_UART0 ); 
	
	
	//添加簡單二次開發協定解析任務
	STS_Add_Task( STS_Task_Trigger_Mode_Custom , 0 , SDI_RCTrigger , SDI_Server );//接收openmv序列槽資料任務
	STS_Add_Task( STS_Task_Trigger_Mode_Custom , 0 , SDI_TXTrigger , SDI_Tx_model_contrl );//發送給openmv序列槽資料任務
}//           	自定義函數觸發       		 時間間隔為0     自定義函數   主函數類型   
           

​ 以上是對tm4c序列槽的簡單配置,具體配置可按個人具體要求來改動,其中兩個STS_Add_Task()函數則是進行資料的接收與發送(Rx、Tx),第一個變量設定觸發函數類型(具有多種觸發類型)為自定義函數觸發,第二個變量為觸發的時間間隔,第三個變量則是自己定義的觸發函數,第四個變量為觸發以後将會執行的主函數。

2.控制Openmv執行指令

uint8_t SDI_mode_contrl = 0;//初始化openmv識别模式  由M35控制
uint8_t SDI_tc_mode = 0;//暫存 SDI_mode_contrl  控制Openmv,選擇将要發送的控制模式

static bool SDI_TXTrigger( unsigned int Task_ID ) //将SDI_mode_contrl存入SDI_tc_mode中
{
	if( SDI_mode_contrl != 0 )
	{
		SDI_tc_mode = SDI_mode_contrl;
		SDI_mode_contrl = 0;//執行一次發送任務即關閉發送
		return true;
	}
	return false; //發送失敗,傳回錯誤
}

static void SDI_Tx_model_contrl( unsigned int Task_ID )  //給予Tx控制模式
{  	//以下為openmv工作模式控制
	uint8_t tc_buf[6] = {0};  //資料緩存到tc_buf[]中,等資料輸入完畢以包的形式發送
	uint8_t tc_cnt = 0;
	uint8_t sum = 0;
	uint16_t tc_data_buf = 0;
	
	tc_buf[0] = 0xAA;//標頭1
	tc_buf[1] = 0x55;//標頭2
	//標頭的存在用于openmv識别,openmv部分會設定接收標頭1、2,隻有當雙方標頭一緻時,才會進行接收資料
	tc_buf[2] = 0x18;//模式位24
	tc_buf[3] = 0x02;//發送資料長度
	switch(SDI_tc_mode) 
	{//openmv指令控制
		case 0xff:
		{
			tc_buf[4] = 0xff;//不識别模式
		}break;
		
		case 33://藍杆測距
		{
			tc_buf[4] = 0x16;
		}break;
		
		case 23://識别藍杆
		{
			tc_buf[4] = 0x2C;
		}break;
		
		case 3://起飛後發送33找橙杆
		{
			tc_buf[4] = 0x21;//開始
		}break;
		
		case 13:																														//B方案3模式:讓openmv進入13模式
		{//  發送11 進入繞杆 橙杆
			tc_buf[4] = 0x0B;//繞杆測距傳回距離
		}break;
     
		case 43:
		{//			巡地線			
			tc_buf[4] = 55;
		}break;
     
		case 101:
		{//起飛完成  可以開始識别色塊轉彎
			tc_buf[4] = 101;
		}break;
     
		case 103:
		{																								//B方案3模式:繞彎B杆後,開始3區巡線
			tc_buf[4] = 103;
		}break;
	}
	tc_buf[5] = (tc_buf[0] + tc_buf[1] + tc_buf[2] + tc_buf[3] + tc_buf[4])%256;
 //        求和取餘作為資料校驗位
	Uart0_Send(tc_buf,6);  //将緩存區的資料輸出
}


           

​ 本段代碼先是定義SDI_tc_mode()用于發送我們想讓openmv執行的模式(以switch的方式選擇,便于後期添加或删除模式),然後再定義tc_buf[6]将標頭等資料全部打包,在最後用Uart0_Send(tc_buf,6)将資料以包的形式輸出openmv。 注意:標頭、包尾是自己喜好定義的,而資料位和資料長度則是按自己對于通信的要求來定義,任務内容也是按自己需要編寫,最後的tc—buf[5]是采取求和取餘的方式來校驗資料是否打包完成。

3.控制輸入

static bool SDI_RCTrigger( unsigned int Task_ID )   //接收觸發位
{
	if( Uart0_DataAvailable() )
		return true;
	return false;
}

//以下按是自己需求編寫的函數變量
uint8_t SDI_west = 0;//為1無人機開始向左移動
uint8_t SDI_color = 0;//為1第一根杆為紅色 為2第一根杆為綠色
uint8_t SDI_sequence = 0;//為1時尋第二個杆 向右移動  ;為2時尋第二個杆 向左移動  ;
uint8_t SDI_area_block=0;//中心區域色塊大小
uint8_t SDI_area_cx=0;//中心區域色塊
uint8_t SDI_forward=0;//為1無人機向前移動
int8_t SDI_delta_x=0;//離杆中心的x軸距離
uint8_t SDI_line_coordinate[3] = {0};//第一位前方y坐标,第二位是中心y坐标,第三位是後方y坐标
int8_t line_angle=0;//拟合的直線角度
uint16_t SDI_poles_distance=0;//飛機與杆的距離
uint16_t SDI_find_pole=0;//為1時表示找到杆
uint16_t msg_pack1;
uint16_t msg_pack2;

static void SDI_Server ( unsigned int Task_ID )
{
	
	/*狀态機變量*/
		static uint8_t rc_step1 = 0;	//0:接收標頭'A' 'C'
	
		#define MAX_SDI_PACKET_SIZE 11       //最大資料包内資料數量
		static uint8_t msg_type;             //資料種類
		static uint8_t msg_length;           //資料長度
		ALIGN4 static uint8_t msg_pack[MAX_SDI_PACKET_SIZE]={0}; //初始化資料包
		static uint8_t sumB;  //求和校驗
		#define reset_SDI_RC ( rc_step1 = rc_step2 = 0 ) 
	/*狀态機變量*/
	uint8_t rc_buf[20];  	//接收包,原理跟發送包一緻
	uint8_t length = read_Uart0( rc_buf , 20 );  //函數在代碼末尾有說明
	for( uint8_t i = 0 ; i < length ; ++i )
	{
		uint8_t r_data = rc_buf[i];    
		switch( rc_step1 )
		{
			case 0 :
				sumB=0;
				if(r_data == 0xAA)
				{
					sumB += r_data;
					rc_step1 = 1;
				}
				break;
			case 1:
				if(r_data == 0x55)
				{
					sumB += r_data;
					rc_step1 = 2;
				}
				else reset_SDI_RC;
				break;
			case 2:
				//接收消息類别
				if(r_data > 0x09 && r_data < 0x30)
				{
					msg_type = r_data; //接收模式類型
					sumB += r_data;
					rc_step1 = 3;
					rc_step2 = 0;
				}
				else reset_SDI_RC;
				break;
			
			case 3:
				//接收消息長度
				if( r_data > MAX_SDI_PACKET_SIZE )
				{
					reset_SDI_RC;
					break;
				}
				msg_length = r_data;
				sumB += r_data;
				rc_step1 = 4;
				rc_step2 = 0;
				break;
				
			case 4:
				//接收資料包
				msg_pack[ rc_step2 ++] = r_data;
				sumB += r_data;
				if( rc_step2 >= msg_length )
				{
					rc_step1 = 5;
					rc_step2 = 0;
				}
				break;			
				
				
			  case 5:
				//接收校驗位
				if(sumB == r_data)
				{
						 if(msg_type == 0x18)//告訴我找到杆
					{
						  Find_redpole1 = msg_pack[0];
					}
					
					else if(msg_type == 0x19)//告訴我找到杆
					{  
						  Find_redpole = msg_pack[2];
						  SDI_delta_x = msg_pack[1];
						  pole_distance = msg_pack[0];
					}
					
				}
	
				
				reset_SDI_RC;		
					
				break;
		}
	}
}

	
//以下是Uart_send的設定
void Uart0_Send( const uint8_t* data , uint16_t length )
{
	IntDisable( INT_UART0 );
	
	//擷取剩餘的緩沖區空間
	int16_t buffer_space = RingBuf_uint8_t_get_Freesize( &Uart0_Tx_RingBuf );
	//擷取DMA中待發送的位元組數
	int16_t DMA_Remain = uDMAChannelSizeGet( UDMA_CH9_UART0TX );
	
	//計算要發送的位元組數
	int16_t max_send_count = buffer_space - DMA_Remain;
	if( max_send_count < 0 )
		max_send_count = 0;
	uint16_t send_count = ( length < max_send_count ) ? length : max_send_count;
	
	//将待發送位元組壓入緩沖區
	RingBuf_uint8_t_push_length( &Uart0_Tx_RingBuf , data , send_count );
//	for( uint8_t i = 0 ; i < send_count ; ++i )
//		RingBuf_uint8_t_push( &Uart0_Tx_RingBuf , data[i] );
	
	//擷取DMA發送是否完成
	if( uDMAChannelIsEnabled( UDMA_CH9_UART0TX ) == false )
	{
		//DMA已完成
		//可以繼續發送
		uint16_t length;
		uint8_t* p = RingBuf_uint8_t_pop_DMABuf( &Uart0_Tx_RingBuf , &length );
		if( length )
		{
			uDMAChannelTransferSet( UDMA_PRI_SELECT | UDMA_CH9_UART0TX , \
				UDMA_MODE_BASIC , p , (void*)&UART0->DR , length );
			uDMAChannelEnable( UDMA_CH9_UART0TX );
		}
	}
	IntEnable( INT_UART0 );
}

static void UART0_Handler()
{
	UARTIntClear( UART0_BASE , UART_INT_OE );
	UARTRxErrorClear( UART0_BASE );
	while( ( UART0->FR & (1<<4) ) == false	)
	{
		//接收
		uint8_t rdata = UART0->DR;
		RingBuf_uint8_t_push( &Uart0_Rx_RingBuf , rdata );
	}
	
	if( uDMAChannelIsEnabled( UDMA_CH9_UART0TX ) == false )
	{
		uint16_t length;
		uint8_t* p = RingBuf_uint8_t_pop_DMABuf( &Uart0_Tx_RingBuf , &length );
		if( length )
		{
			uDMAChannelTransferSet( UDMA_PRI_SELECT | UDMA_CH9_UART0TX , \
				UDMA_MODE_BASIC , p , (void*)&UART0->DR , length );
			uDMAChannelEnable( UDMA_CH9_UART0TX );
		}
	}
}

uint16_t read_Uart0( uint8_t* data , uint16_t length )
{
	IntDisable( INT_UART0 );
	uint8_t read_bytes = RingBuf_uint8_t_pop_length( &Uart0_Rx_RingBuf , data , length );
	IntEnable( INT_UART0 );
	return read_bytes;
}

uint16_t Uart0_DataAvailable()
{
	IntDisable( INT_UART0 );
	uint16_t bytes2read = RingBuf_uint8_t_get_Bytes2read( &Uart0_Rx_RingBuf );
	IntEnable( INT_UART0 );
	return bytes2read;
}
           

​ 接收原理與發送原理一緻,首先是識别標頭,然後确定接收到的模式,用于接收對應模式所需要傳達的資訊。

7.26學習筆記