目录
一、自动巡航展示
二、项目技术点
RTOS:采用freertos作为多线程运行架构,在项目中使用临界区和互斥量对其初始化过程和外设部分进行保护。使用信号量实现通知任务目的,这里展示部分,有的函数被传入做回调函数。
/**
* @brief 用于Give到<定位任务>信号量
*
*/
void give_locating_binary(void)
{
xSemaphoreGive(locating_Binary_Handle);
}
/**
* @brief 用于Tack到<定位任务>信号量
*
*/
void take_locating_binary(void)
{
xSemaphoreTake(locating_Binary_Handle, portMAX_DELAY);
}
/**
* @brief 用于Give到<获取数据任务>信号量
*
*/
void give_getd_binary(void)
{
xSemaphoreGive(getd_Binary_Handle);
}
/**
* @brief 用于Tack到<获取数据任务>信号量
*
*/
void take_getd_binary(void)
{
xSemaphoreTake(getd_Binary_Handle, portMAX_DELAY);
}
void give_Pidturn_binary(void)
{
xSemaphoreGive(Pidturn_Binary_Handle);
}
/**
* @brief 用于Give到<通信任务>信号量
*
*/
void give_m780eg_uart_binary(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR( m780_uart_Binary_Handle, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
/**
* @brief 用于Tack到<通信任务>信号量
*
*/
void take_m780eg_uart_binary(void)
{
xSemaphoreTake(m780_uart_Binary_Handle, portMAX_DELAY);
}
面向对象:初始化阶段使用大量面向对象写法,将其定义为只读数据区,进行内存优化,并将其通过抽象封装好的函数进行初始化,减少代码复用。
//通讯指令数组
const rx_datacom_t rx_datacom[RXD_CODE_LENGTH] = {
/*app-->manual*/
{APP_UP ,"UP" ,2 ,rxd_app_manual_fun},
{APP_BOTTOM ,"BOTTOM" ,6 ,rxd_app_manual_fun},
{APP_LEFT ,"LEFT" ,4 ,rxd_app_manual_fun},
{APP_RIGHT ,"RIGHT" ,6 ,rxd_app_manual_fun},
{APP_STOP ,"STOP" ,4 ,rxd_app_manual_fun},
/*app-->command*/
{APP_ACQUIRE ,"ACQUIRE" ,7 ,rxd_app_command_fun},
/*CHECK --> 检查 进行校准 检查传感器初始化状态*/
{APP_CHECK ,"CHECK" ,5 ,rxd_boat_check_fun},
/*紧急停止*/
{APP_EMER_STOP ,"EMERSTOP" ,8 ,rxd_boat_emer_stop_fun},
/*恢复停止*/
{APP_RESUME_STOP ,"RESSTOP" ,7 ,rxd_boat_Resume_fun},
{APP_GET_GPS ,"GETGPS" ,6 ,rxd_app_command_fun},
/*app-->gps locating*/
{APP_GPS_AXIS ,"F1" ,2 ,rxd_app_locating_fun},
/*dtu-->command Rec*/
/*DTU_REC_CSQ --> 信号质量data DTU_REC_GPS --> GPS定位数据*/
{DTU_REC_CSQ ,"\r\nconfig,csq,ok," ,16 ,rxd_dtu_rec_command_fun},
{DTU_REC_REBOOT ,"\r\nconfig,reboot,ok\r\n" ,20 ,rxd_dtu_rec_command_fun},
// {DTU_REC_GPSEXT ,"\r\nconfig,gps,ok," ,16 ,rxd_dtu_rec_command_fun},
{DTU_REC_GPS ,"\r\nconfig,gps,ok," ,16 ,rxd_dtu_rec_gps_fun},
};
//调试指令数组
const rx_command_t rx_command_buff[10] = {
{"help" , rx_command_help_fun}, //帮助
{"showhc" , rx_command_showhc_fun}, //查看电子罗盘校准值
{"showgps" , rx_command_show_gps_fun}, //查看当前定位信息
{"con:" , rx_command_control_fun}, //控制电机
{"check" , rx_command_boat_check_fun}, //校准
{"set:" , rx_command_set_angle_fun}, //设置角度
{"pidt" , rx_command_take_Pidturn_Binary}, //执行PID
{"reset" , rx_command_SystemReset_fun}, //通过软件复位
{"sus" , rx_command_emer_stop_fun}, //挂起 - 紧急停止
{"res" , rx_command_resume_stop_fun}, //恢复
};
//通讯任务 遍历相关指令并执行对应函数指针
void dispose_Task(void *p)
{
uint8_t rxd_ret = 0;
register_m780eg_Binary_Handler(give_m780eg_uart_binary);
m780_uart_Binary_Handle = xSemaphoreCreateBinary();
locating_Binary_Handle = xSemaphoreCreateBinary();
while (1)
{
xSemaphoreTake(m780_uart_Binary_Handle, portMAX_DELAY);
{
rbklen = readFromFIFO(&m780eg_rec_t,rbkbuff,148);
printf("buff = %s \r\nlen = %d \r\n", rbkbuff,rbklen);
for(uint8_t rxd_len = 0 ; rxd_len < RXD_CODE_LENGTH ; rxd_len++)
{
rxd_ret = rxd_find_code_fun(&rx_datacom[rxd_len]);
VOID_CHECK_FLAGS(rxd_ret);
}
memset(rbkbuff,0x00,sizeof(rbkbuff));
}
}
}
const dri_I2C2_t dri_I2C2 = {
{ .pin = "PB10,PB11",
.mode = GPIO_Mode_AF,
.pupd = GPIO_PuPd_NOPULL,
.otype = GPIO_OType_OD,
.speed = GPIO_Speed_50MHz,
.AFpin = GPIO_AF_I2C2, },
{ .i2c_ClkSpeed = 400000,
.i2C_DuCycle = I2C_DutyCycle_2,
.i2c_AckAddress = I2C_AcknowledgedAddress_7bit,
.i2c_OwnAddress = 0x30,
.i2c = I2C2,
},
};
bool dev_io_config(dev_gpio_t *drv_gpio)
{
dev_gpio_pt gpio_pint={0};
GPIO_InitTypeDef GPIO_InitStructure;
gain_gpio_pin(drv_gpio->pin,&gpio_pint); //解析字符串 如"PA8,PA9"
GPIO_InitStructure.GPIO_Pin = gpio_pint.gpioPin[0] | gpio_pint.gpioPin[1];
GPIO_InitStructure.GPIO_Mode = drv_gpio->mode;
GPIO_InitStructure.GPIO_Speed = drv_gpio->speed;
GPIO_InitStructure.GPIO_OType = drv_gpio->otype;
GPIO_InitStructure.GPIO_PuPd = drv_gpio->pupd;
GPIO_Init(gpio_pint.gpiot, &GPIO_InitStructure);
if(drv_gpio->AFpin != AF_DISABLE_FLG)
{
switch (gpio_pint.pincnt)
{
case 1:GPIO_PinAFConfig(gpio_pint.gpiot, (uint8_t)gpio_pint.gpioPinSour[1] , drv_gpio->AFpin);
case 0:GPIO_PinAFConfig(gpio_pint.gpiot, (uint8_t)gpio_pint.gpioPinSour[0] , drv_gpio->AFpin);
break;
}
}
return true;
}
bool dev_i2c_config(dev_i2c_t *drv_i2ct)
{
I2C_InitTypeDef I2C_InitStructure;
I2C_DeInit(drv_i2ct->i2c);
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress =drv_i2ct->i2c_AckAddress;
I2C_InitStructure.I2C_ClockSpeed = drv_i2ct->i2c_ClkSpeed;
I2C_InitStructure.I2C_DutyCycle =drv_i2ct->i2C_DuCycle;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = drv_i2ct->i2c_OwnAddress;
I2C_Init(drv_i2ct->i2c,&I2C_InitStructure);
I2C_Cmd(drv_i2ct->i2c,ENABLE);
return true;
}
三、项目架构
用户端:能够实现自动巡航、手动控制、查看水质数据、实时定位信息
云平台:将用户端和设备端进行数据交互,做数据流转。
设备端:
指令Task:开发调试任务,遍历相关指令执行相关操作。
通讯Task:负责执行相关任务以及同步相关线程(信号量),包括校准、手动控制等。
上传数据Task:这里负责采集水质数据,以状态机的架构进行采集,包括采集数据、均值滤波、打包数据、上传数据、清空缓存状态机制。
自动巡航Task:处理坐标、实现直行PID、通知转向PID、通知采集数据。
转向Task:负责转向处理。
船体状态Task:负责避障工作和船体姿态检测工作。
四、下位机架构
该项目采用分层架构设计,实现应用层和驱动层分离,驱动层的修改只会影响少量的应用层。