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學習筆記