Openmv与tm4c通信笔记:
首先,用个例子来简单理理通信的步骤:
假如我现在想要识别红杆,先要在主函数里写入对应的发送模式(这里假设 写入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学习笔记