手把手教你用STM32F407开发板手搓一个四轴无人机(无刷电机版本)

目录

一、背景

二、整体架构

三、硬件设计 

1、电源管理

2、STM32最小系统​编辑

3、IMU九轴姿态传感器+气压计

4、2.4G无线通信模块​编辑

5、SBUS、串口等其他外围电路​编辑

四、软件设计

1、main创建各任务函数 

2、陀螺仪、加速度计姿态数据解算函数 

3、PID控制函数 

4、匿名上位机 

5、NRF24L01无线模块 

6、GPS数据解析

7、气压计数据解析 

8、SBUS数据解析

9、超声波数据解析

五、总结 


一、背景

        前段时间帮我师弟做了一个“无人机喷药”的项目,时间比较仓促,做的也比较简陋,毕竟是毕设嘛,比起外观,更注重功能的完整性。他是自动化专业,之前学过一些电子电路、微机原理、C语言的课程,但是没有相关项目的开发经验,要求我手把手教他完成这个项目,一方面能顺利毕业,另一方面也为今后找工作做铺垫,整个项目前后花了将近3个月的时间。下面我把整个开发过程做了一个详细的记录,有相关需求的小伙伴可以做个借鉴或私信我进行交流,有不对的地方欢迎大家批评并指正。(这里要特别感谢正点原子、小马哥以及匿名科创团队,整个项目得以顺利完成,是因为我站在了巨人的肩膀上。)

哔哩哔哩视频效果演示:https://www.bilibili.com/video/BV1aP4y137b3/?vd_source=646ab3ffb3c97fca658a999badabbc66

二、整体架构

        整个无人机主要由以下几部分组成:机架、飞控、遥控器和地面站。其中机架可以根据自己的需求自行进行设计并3D打印,飞控和遥控器可以自己画PCB,自己焊接。(后文有飞控和遥控器全部的原理图、PCB源文件和源代码)

        如果毕设学校不给报销,或者经费不足,飞控可以直接用单片机代替,遥控器可以用地面站(PC端或者手机端)代替,另外再买个6轴姿态传感器和无线透传模块即可,这样可以节省很多费用和时间。推荐组合:正点原子核心板+匿名科创地面站+MPU6050模块+无线模块。

F330机架
STM32F407最小系统板
地面站

三、硬件设计 

        四旋翼无人机主要由检测模块、控制模块、驱动模块以及电源模块四个部分组成。

1、电源管理

2、STM32最小系统

3、IMU九轴姿态传感器+气压计

4、2.4G无线通信模块

5、SBUS、串口等其他外围电路

四、软件设计

        软件采用FreeRTOS操作系统和裸机两种方式开发,提供了:①PID控制、LPF、滑动窗口滤波等多种无人机相关飞行控制算法;②陀螺仪、加速度计、气压计、超声波、NRF24L01、定时器PWM、GPS、蓝牙、WiFi等多种传感器的驱动;③匿名地面站、SBUS等多种遥控遥测协议的数据解析。所有代码均开源,尤其适合初学者。

1、main创建各任务函数 

//姿态控制任务函数
void ctl_task(void *pvParameters)
{
	while(1)
	{ 
		
		PID_Calculate();
		UAV_Control();		
		vTaskDelay(4);                        			
	}
}

//姿态解算任务函数
void imu_task(void *pvParameters)
{
	TickType_t lastWakeTime = xTaskGetTickCount();
	while(1)
	{
		Read_MPU6050_RawData();
		Get_MPU6050_Value();
		vTaskDelayUntil(&lastWakeTime,(DeltaT*1000));  //2ms                       
	}
}

//高度控制任务函数
void high_task(void *pvParameters)
{ 
	while(1)
	{		
		Get_BMP280_Value();
		vTaskDelay(10);                          
	}
}

//遥控遥测任务函数
void controller_task(void *pvParameters)
{
	while(1)
	{
		Get_NRF24l01_Value();
		ANO_DT_Data_Exchange();
		#if 0
		vTaskList(TaskInformationList);
		printf("\r\n%s\r\n",TaskInformationList);
		#endif		
		vTaskDelay(10);
	}
}

//GPS任务函数
void gps_task(void *pvParameters)
{ 
	while(1)
	{		
		Get_GPS_Value();
		vTaskDelay(100-3);                          
	}
}

//flash数据存储任务函数
void flash_task(void *pvParameters)
{ 
	while(1)
	{		
		Flash_PID_Param_Update();
	}
}

//匿名参数设置任务函数
void ano_task(void *pvParameters)
{ 
	while(1)
	{		
		ANO_DataPack_Rx();
	}
}

2、陀螺仪、加速度计姿态数据解算函数 

void Read_MPU6050_RawData(void)
{
	vTaskSuspendAll();
	MPU6050_Read_RawData();
	xTaskResumeAll();
	
	MPU6050_Calibrate();  //对MPU6050进行处理,减去零偏。
	
	SortAver_FilterXYZ(&MPU6050_ACC_RAW,&Acc_filt,12);//对加速度原始数据进行去极值滑动窗口滤波
	
	//加速度AD值 转换成 米/平方秒 
	Acc_filt.X = (float)Acc_filt.X * Acc_Gain * G;
	Acc_filt.Y = (float)Acc_filt.Y * Acc_Gain * G;
	Acc_filt.Z = (float)Acc_filt.Z * Acc_Gain * G;
	
	//陀螺仪AD值 转换成 弧度/秒    
	Gyr_rad.X = (float) MPU6050_GYRO_RAW.X * Gyro_Gr;  
	Gyr_rad.Y = (float) MPU6050_GYRO_RAW.Y * Gyro_Gr;
	Gyr_rad.Z = (float) MPU6050_GYRO_RAW.Z * Gyro_Gr;
	
	Acc_filt.X = Acc_filt.X * Kp_New + Acc_filtold.X * Kp_Old;
	Acc_filt.Y = Acc_filt.Y * Kp_New + Acc_filtold.Y * Kp_Old;
	Acc_filt.Z = Acc_filt.Z * Kp_New + Acc_filtold.Z * Kp_Old;

	
	Acc_filtold.X =  Acc_filt.X;
	Acc_filtold.Y =  Acc_filt.Y;
	Acc_filtold.Z =  Acc_filt.Z;


	accb[0] = Acc_filt.X;
	accb[1] = Acc_filt.Y;
	accb[2] = Acc_filt.Z;
}


float q0 = 1, q1 = 0, q2 = 0, q3 = 0; 
float   DCMgb[3][3];                  //方向余弦阵(将 惯性坐标系 转化为 机体坐标系)


void IMUupdate(FLOAT_XYZ *Gyr_rad,FLOAT_XYZ *Acc_filt,FLOAT_ANGLE *Att_Angle)
{
	uint8_t i;
	float matrix[9] = {1.f,  0.0f,  0.0f, 0.0f,  1.f,  0.0f, 0.0f,  0.0f,  1.f };//初始化矩阵
	float ax = Acc_filt->X,ay = Acc_filt->Y,az = Acc_filt->Z;
	float gx = Gyr_rad->X,gy = Gyr_rad->Y,gz = Gyr_rad->Z;
	float vx, vy, vz;
	float ex, ey, ez;
	float norm;

	float q0q0 = q0*q0;
	float q0q1 = q0*q1;
	float q0q2 = q0*q2;
	float q0q3 = q0*q3;
	float q1q1 = q1*q1;
	float q1q2 = q1*q2;
	float q1q3 = q1*q3;
	float q2q2 = q2*q2;
	float q2q3 = q2*q3;
	float q3q3 = q3*q3;

	if(ax*ay*az==0)
	return;

	//加速度计测量的重力向量(机体坐标系) 
	norm = invSqrt(ax*ax + ay*ay + az*az); 
	ax = ax * norm;
	ay = ay * norm;
	az = az * norm;
	//	printf("ax=%0.2f ay=%0.2f az=%0.2f\r\n",ax,ay,az);

	//陀螺仪积分估计重力向量(机体坐标系) 
	vx = 2*(q1q3 - q0q2);												
	vy = 2*(q0q1 + q2q3);
	vz = q0q0 - q1q1 - q2q2 + q3q3 ;
	// printf("vx=%0.2f vy=%0.2f vz=%0.2f\r\n",vx,vy,vz); 

	//测量的重力向量与估算的重力向量差积求出向量间的误差 
	ex = (ay*vz - az*vy); //+ (my*wz - mz*wy);                     
	ey = (az*vx - ax*vz); //+ (mz*wx - mx*wz);
	ez = (ax*vy - ay*vx); //+ (mx*wy - my*wx);

	//用上面求出误差进行积分
	exInt = exInt + ex * Ki;								 
	eyInt = eyInt + ey * Ki;
	ezInt = ezInt + ez * Ki;

	//将误差PI后补偿到陀螺仪
	gx = gx + Kp*ex + exInt;					   		  	
	gy = gy + Kp*ey + eyInt;
	gz = gz + Kp*ez + ezInt;

	//四元素的微分方程
	q0 = q0 + (-q1*gx - q2*gy - q3*gz)*halfT;
	q1 = q1 + (q0*gx + q2*gz - q3*gy)*halfT;
	q2 = q2 + (q0*gy - q1*gz + q3*gx)*halfT;
	q3 = q3 + (q0*gz + q1*gy - q2*gx)*halfT;

	//单位化四元数 
	norm = invSqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
	q0 = q0 * norm;
	q1 = q1 * norm;
	q2 = q2 * norm;  
	q3 = q3 * norm;

	//矩阵R将惯性坐标系转换到机体坐标系 
	matrix[0] = q0q0 + q1q1 - q2q2 - q3q3;	
	matrix[1] = 2.f * (q1q2 + q0q3);	    
	matrix[2] = 2.f * (q1q3 - q0q2);	    
	matrix[3] = 2.f * (q1q2 - q0q3);	    
	matrix[4] = q0q0 - q1q1 + q2q2 - q3q3;	
	matrix[5] = 2.f * (q2q3 + q0q1);	    
	matrix[6] = 2.f * (q1q3 + q0q2);	    
	matrix[7] = 2.f * (q2q3 - q0q1);	    
	matrix[8] = q0q0 - q1q1 - q2q2 + q3q3;	

	//四元数转换成欧拉角
	Att_Angle->yaw = atan2(2.f * (q1q2 + q0q3), q0q0 + q1q1 - q2q2 - q3q3)* 57.3f; // yaw
	Att_Angle->rol = -asin(2.f * (q1q3 - q0q2))* 57.3f;  // roll
	Att_Angle->pit =  atan2(2.f * q2q3 + 2.f * q0q1, q0q0 - q1q1 - q2q2 + q3q3)* 57.3f ; // pitch
	for(i=0;i<9;i++)
	{
		*(&(DCMgb[0][0])+i) = matrix[i];
	}
}

3、PID控制函数 

void PID_Init(struct PID *pid,float kp,float ki,float kd,float Irange,float Ilimit)
{
	 pid->Kp=kp; 
	 pid->Ki=ki; 
	 pid->Kd=kd;
	 pid->IntegralRange=Irange; 
	 pid->IntegralLimit=Ilimit;  
}
 


void PID_Control(struct PID *pid,float SetValue,float ActualValue)
{
	pid->Error=SetValue-ActualValue;
	
	if( fabs(pid->Error)>pid->IntegralRange || RC_Control.THR<=THROTTLE_MIN)
	{
		pid->IntegralFlag=0;
	}
	else
	{
		pid->IntegralFlag=1;
		pid->SumIntegral+=pid->Error; 
		if(pid->SumIntegral>pid->IntegralLimit)  pid->SumIntegral=pid->IntegralLimit;
		if(pid->SumIntegral<-pid->IntegralLimit) pid->SumIntegral=-pid->IntegralLimit;
	}
	
	pid->Pout=pid->Kp*pid->Error;
	pid->Iout=pid->IntegralFlag*pid->Ki*pid->SumIntegral;
	pid->Dout=pid->Kd*(pid->Error-pid->PreError);
	
	pid->OutPut=pid->Pout+pid->Iout+pid->Dout;
	
	pid->PreError=pid->Error;  
	
	#if 0
	printf("PID输出为:%.2f\r\n",pid->OutPut);
	#endif
}


void PID_Calculate(void)
{	
	#if 1
	TargetAngle.Rol=(RC_Control.ROL-1500)/25.0f;
	MeasureAngle.Rol=Att_Angle.rol;
	MeasureRate.RolRate=Gyr_rad.X*RadtoDeg;
	PID_Control(&PID_ROL_Angle,TargetAngle.Rol,MeasureAngle.Rol);
	PID_Control(&PID_ROL_Rate,PID_ROL_Angle.OutPut,MeasureRate.RolRate);
	#endif
	
	#if 1
	TargetAngle.Pit=(RC_Control.PIT-1500)/25.0f;	
	MeasureAngle.Pit=Att_Angle.pit;	
	MeasureRate.PitRate=Gyr_rad.Y*RadtoDeg;	
	PID_Control(&PID_PIT_Angle,TargetAngle.Pit,MeasureAngle.Pit);
	PID_Control(&PID_PIT_Rate,PID_PIT_Angle.OutPut,MeasureRate.PitRate);
	#endif
	
	#if 1
	TargetAngle.Yaw=(1500-RC_Control.YAW)/20.0f; 			
	MeasureAngle.Yaw=Att_Angle.yaw; 		
	MeasureRate.YawRate=Gyr_rad.Z*RadtoDeg; 
	PID_Control(&PID_YAW_Angle,TargetAngle.Yaw,MeasureAngle.Yaw);
	PID_Control(&PID_YAW_Rate,PID_YAW_Angle.OutPut,MeasureRate.YawRate);
	#endif
	
	#if 1
	TargetAngle.Alt=0;
	MeasureAngle.Alt=0;
	MeasureRate.AltRate=0;
	PID_Control(&PID_ALT,TargetAngle.Alt,MeasureAngle.Alt);
	PID_Control(&PID_ALT_Rate,PID_ALT.OutPut,MeasureRate.AltRate);
	#endif
	
		
	#if 0
	printf("Rol测量值:%.3f,%.3f,%.3f\r\n",TargetAngle.Rol,MeasureAngle.Rol,MeasureRate.RolRate);
	printf("Pit测量值:%.3f,%.3f,%.3f\r\n",TargetAngle.Pit,MeasureAngle.Pit,MeasureRate.PitRate);
	printf("Yaw测量值:%.3f,%.3f,%.3f\r\n",TargetAngle.Yaw,MeasureAngle.Yaw,MeasureRate.YawRate);
	printf("Thr测量值:%d\r\n",RC_Control.THR);
	#endif
}


void UAV_Control(void)
{
	static u8 DisableFlag=0;
	if(RC_Control.THR>THROTTLE_MIN)
	{
		Moto_PWM_1=RC_Control.THR+PID_ALT_Rate.OutPut+PID_ROL_Rate.OutPut+PID_PIT_Rate.OutPut+PID_YAW_Rate.OutPut+OFFSET_M1;
		Moto_PWM_2=RC_Control.THR+PID_ALT_Rate.OutPut-PID_ROL_Rate.OutPut+PID_PIT_Rate.OutPut-PID_YAW_Rate.OutPut+OFFSET_M2;
		Moto_PWM_3=RC_Control.THR+PID_ALT_Rate.OutPut-PID_ROL_Rate.OutPut-PID_PIT_Rate.OutPut+PID_YAW_Rate.OutPut+OFFSET_M3;
		Moto_PWM_4=RC_Control.THR+PID_ALT_Rate.OutPut+PID_ROL_Rate.OutPut-PID_PIT_Rate.OutPut-PID_YAW_Rate.OutPut+OFFSET_M4;	
	}
	else
	{
		Moto_PWM_1 = MOTOR_PWM_MIN;
		Moto_PWM_2 = MOTOR_PWM_MIN;
		Moto_PWM_3 = MOTOR_PWM_MIN;
		Moto_PWM_4 = MOTOR_PWM_MIN;
	}
	if(DisableFlag==1)
	{
		Moto_PWM_1 = MOTOR_PWM_MIN;
		Moto_PWM_2 = MOTOR_PWM_MIN;
		Moto_PWM_3 = MOTOR_PWM_MIN;
		Moto_PWM_4 = MOTOR_PWM_MIN;
	}	
	
	Moto_PWM_1=LIMIT(Moto_PWM_1,MOTOR_PWM_MIN,MOTOR_PWM_MAX);
	Moto_PWM_2=LIMIT(Moto_PWM_2,MOTOR_PWM_MIN,MOTOR_PWM_MAX);
	Moto_PWM_3=LIMIT(Moto_PWM_3,MOTOR_PWM_MIN,MOTOR_PWM_MAX);
	Moto_PWM_4=LIMIT(Moto_PWM_4,MOTOR_PWM_MIN,MOTOR_PWM_MAX);
	
	TIM_SetCompare1(TIM3,Moto_PWM_1);
	TIM_SetCompare2(TIM3,Moto_PWM_2);
	TIM_SetCompare3(TIM3,Moto_PWM_3);
	TIM_SetCompare4(TIM3,Moto_PWM_4);
}

4、匿名上位机 

extern INT16_XYZ  GYRO_OFFSET_RAW,ACC_OFFSET_RAW; //零漂数据
extern xSemaphoreHandle PIDWriteFlashFlag;
void ANO_DT_Data_Receive_Anl(uint8_t *data_buf,uint8_t num)
{
	uint8_t sum = 0,i;
	for(i=0;i<(num-1);i++)
		sum += *(data_buf+i);
	if(!(sum==*(data_buf+num-1)))		return;		//判断sum
	if(!(*(data_buf)==0xAA && *(data_buf+1)==0xAF))		return;		//判断帧头
	
	if(*(data_buf+2)==0X01)
	{
		if(*(data_buf+4)==0X01)
		{
			SENSER_FLAG_SET(ACC_OFFSET);//加速度校准
			ACC_OFFSET_RAW.X = 0;ACC_OFFSET_RAW.Y = 0;ACC_OFFSET_RAW.Z = 0;
		}
		if(*(data_buf+4)==0X02)
		{
			SENSER_FLAG_SET(GYRO_OFFSET);//陀螺仪校准
			GYRO_OFFSET_RAW.X = 0;GYRO_OFFSET_RAW.Y = 0;GYRO_OFFSET_RAW.Z = 0;
		}
		if(*(data_buf+4)==0X03)
		{
			SENSER_FLAG_SET(ACC_OFFSET);//加速度校准
			SENSER_FLAG_SET(GYRO_OFFSET);//陀螺仪校准
		}
		if(*(data_buf+4)==0X04)
		{
			
		}
		if(*(data_buf+4)==0X05)
		{
			SENSER_FLAG_SET(BAR_OFFSET);//气压计校准
		}
	}
	
	if(*(data_buf+2)==0X02)
	{
		if(*(data_buf+4)==0X01)
		{
			f.send_pid1 = 1;//读取PID1
			f.send_pid2 = 1;//读取PID2
			f.send_pid3 = 1;//读取PID3
		}
		if(*(data_buf+4)==0X02)
		{
			
		}
		if(*(data_buf+4)==0XA0)		//读取版本信息
		{
			f.send_version = 1;
		}
		if(*(data_buf+4)==0XA1)		//恢复默认参数
		{
			f.send_pid4 = 1;        //参数清零
		}
	}

	if(*(data_buf+2)==0X10)								//PID1
    {
        PID_ROL_Rate.Kp = 0.001*( (vs16)(*(data_buf+4)<<8)|*(data_buf+5) );
        PID_ROL_Rate.Ki = 0.001*( (vs16)(*(data_buf+6)<<8)|*(data_buf+7) );
        PID_ROL_Rate.Kd = 0.001*( (vs16)(*(data_buf+8)<<8)|*(data_buf+9) );
        PID_PIT_Rate.Kp = 0.001*( (vs16)(*(data_buf+10)<<8)|*(data_buf+11) );
        PID_PIT_Rate.Ki = 0.001*( (vs16)(*(data_buf+12)<<8)|*(data_buf+13) );
        PID_PIT_Rate.Kd = 0.001*( (vs16)(*(data_buf+14)<<8)|*(data_buf+15) );
        PID_YAW_Rate.Kp = 0.001*( (vs16)(*(data_buf+16)<<8)|*(data_buf+17) );
        PID_YAW_Rate.Ki = 0.001*( (vs16)(*(data_buf+18)<<8)|*(data_buf+19) );
        PID_YAW_Rate.Kd = 0.001*( (vs16)(*(data_buf+20)<<8)|*(data_buf+21) );
          
		ANO_DT_Send_Check(*(data_buf+2),sum);
    }
    if(*(data_buf+2)==0X11)								//PID2
    {
        PID_ROL_Angle.Kp 	= 0.001*( (vs16)(*(data_buf+4)<<8)|*(data_buf+5) );
        PID_ROL_Angle.Ki 	= 0.001*( (vs16)(*(data_buf+6)<<8)|*(data_buf+7) );
        PID_ROL_Angle.Kd 	= 0.001*( (vs16)(*(data_buf+8)<<8)|*(data_buf+9) );
        PID_PIT_Angle.Kp 	= 0.001*( (vs16)(*(data_buf+10)<<8)|*(data_buf+11) );
        PID_PIT_Angle.Ki 	= 0.001*( (vs16)(*(data_buf+12)<<8)|*(data_buf+13) );
        PID_PIT_Angle.Kd 	= 0.001*( (vs16)(*(data_buf+14)<<8)|*(data_buf+15) );
        PID_YAW_Angle.Kp	= 0.001*( (vs16)(*(data_buf+16)<<8)|*(data_buf+17) );
        PID_YAW_Angle.Ki 	= 0.001*( (vs16)(*(data_buf+18)<<8)|*(data_buf+19) );
        PID_YAW_Angle.Kd 	= 0.001*( (vs16)(*(data_buf+20)<<8)|*(data_buf+21) );
			 
			 ANO_DT_Send_Check(*(data_buf+2),sum);
    }
  if(*(data_buf+2)==0X12)								//PID3
  {	
		PID_ALT_Rate.Kp 	= 0.001*( (vs16)(*(data_buf+4)<<8)|*(data_buf+5) );
		PID_ALT_Rate.Ki 	= 0.001*( (vs16)(*(data_buf+6)<<8)|*(data_buf+7) );
		PID_ALT_Rate.Kd 	= 0.001*( (vs16)(*(data_buf+8)<<8)|*(data_buf+9) );
		PID_ALT.Kp       	= 0.001*( (vs16)(*(data_buf+10)<<8)|*(data_buf+11) );
		PID_ALT.Ki 	        = 0.001*( (vs16)(*(data_buf+12)<<8)|*(data_buf+13) );
		PID_ALT.Kd       	= 0.001*( (vs16)(*(data_buf+14)<<8)|*(data_buf+15) );;
	  
    ANO_DT_Send_Check(*(data_buf+2),sum);
  }
	if(*(data_buf+2)==0X13)								
	{
		
	}
	if(*(data_buf+2)==0X14)								
	{
		
	}
	if(*(data_buf+2)==0X15)								
	{
		xSemaphoreGive(PIDWriteFlashFlag);
	}
}

5、NRF24L01无线模块 

u8 NRF24L01_TxPacket(u8 *txbuf)
{
	u8 sta;
 	SPI1_SetSpeed(SPI_BaudRatePrescaler_16);//spi速度为10.5Mhz(24L01的最大SPI时钟为10Mhz)   
	NRF24L01_CE=0;
    NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
 	NRF24L01_CE=1;//启动发送	   
	while(NRF24L01_IRQ!=0);//等待发送完成
	sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值	   
	NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
	if(sta&MAX_TX)//达到最大重发次数
	{
		NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器 
		return MAX_TX; 
	}
	if(sta&TX_OK)//发送完成
	{
		return TX_OK;
	}
	return 0xff;//其他原因发送失败
}


u8 NRF24L01_RxPacket(u8 *rxbuf)
{
	u8 sta;		    							   
	SPI1_SetSpeed(SPI_BaudRatePrescaler_16); //spi速度为10.5Mhz(24L01的最大SPI时钟为10Mhz)   
	sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值    	 
	NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
	//printf("0x%02x\r\n",sta);
	if(sta&RX_OK)//接收到数据
	{
		NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
		NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器 
		return 0; 
	}	   
	return 1;//没收到任何数据
}					    


void NRF24L01_RX_Mode(void)
{
  NRF24L01_CE=0;	  
  NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址
	  
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);    //使能通道0的自动应答    
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);//使能通道0的接收地址  	 
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,45);	     //设置RF通信频率		  
  NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度 	    
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
  NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 
  NRF24L01_CE = 1; //CE为高,进入接收模式 
}						 


void NRF24L01_TX_Mode(void)
{														 
  NRF24L01_CE=0;	    
  NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址 
  NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK	  

  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
  NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,45);       //设置RF通道为40
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
  NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
  NRF24L01_CE=1;//CE为高,10us后启动发送
}

6、GPS数据解析

void NMEA_BDGSV_Analysis(nmea_msg *gpsx,u8 *buf)
{
	u8 *p,*p1,dx;
	u8 len,i,j,slx=0;
	u8 posx;   	 
	p=buf;
	p1=(u8*)strstr((const char *)p,"$BDGSV");
	len=p1[7]-'0';								//得到BDGSV的条数
	posx=NMEA_Comma_Pos(p1,3); 					//得到可见北斗卫星总数
	if(posx!=0XFF)gpsx->beidou_svnum=NMEA_Str2num(p1+posx,&dx);
	for(i=0;i<len;i++)
	{	 
		p1=(u8*)strstr((const char *)p,"$BDGSV");  
		for(j=0;j<4;j++)
		{	  
			posx=NMEA_Comma_Pos(p1,4+j*4);
			if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_num=NMEA_Str2num(p1+posx,&dx);	//得到卫星编号
			else break; 
			posx=NMEA_Comma_Pos(p1,5+j*4);
			if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_eledeg=NMEA_Str2num(p1+posx,&dx);//得到卫星仰角 
			else break;
			posx=NMEA_Comma_Pos(p1,6+j*4);
			if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_azideg=NMEA_Str2num(p1+posx,&dx);//得到卫星方位角
			else break; 
			posx=NMEA_Comma_Pos(p1,7+j*4);
			if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_sn=NMEA_Str2num(p1+posx,&dx);	//得到卫星信噪比
			else break;
			slx++;	   
		}   
 		p=p1+1;//切换到下一个BDGSV信息
	}   
}


void NMEA_GNGGA_Analysis(nmea_msg *gpsx,u8 *buf)
{
	u8 *p1,dx;			 
	u8 posx;    
	p1=(u8*)strstr((const char *)buf,"$GNGGA");
	posx=NMEA_Comma_Pos(p1,6);								//得到GPS状态
	if(posx!=0XFF)gpsx->gpssta=NMEA_Str2num(p1+posx,&dx);	
	posx=NMEA_Comma_Pos(p1,7);								//得到用于定位的卫星数
	if(posx!=0XFF)gpsx->posslnum=NMEA_Str2num(p1+posx,&dx); 
	posx=NMEA_Comma_Pos(p1,9);								//得到海拔高度
	if(posx!=0XFF)gpsx->altitude=NMEA_Str2num(p1+posx,&dx);  
}


void NMEA_GNGSA_Analysis(nmea_msg *gpsx,u8 *buf)
{
	u8 *p1,dx;			 
	u8 posx; 
	u8 i;   
	p1=(u8*)strstr((const char *)buf,"$GNGSA");
	posx=NMEA_Comma_Pos(p1,2);								//得到定位类型
	if(posx!=0XFF)gpsx->fixmode=NMEA_Str2num(p1+posx,&dx);	
	for(i=0;i<12;i++)										//得到定位卫星编号
	{
		posx=NMEA_Comma_Pos(p1,3+i);					 
		if(posx!=0XFF)gpsx->possl[i]=NMEA_Str2num(p1+posx,&dx);
		else break; 
	}				  
	posx=NMEA_Comma_Pos(p1,15);								//得到PDOP位置精度因子
	if(posx!=0XFF)gpsx->pdop=NMEA_Str2num(p1+posx,&dx);  
	posx=NMEA_Comma_Pos(p1,16);								//得到HDOP位置精度因子
	if(posx!=0XFF)gpsx->hdop=NMEA_Str2num(p1+posx,&dx);  
	posx=NMEA_Comma_Pos(p1,17);								//得到VDOP位置精度因子
	if(posx!=0XFF)gpsx->vdop=NMEA_Str2num(p1+posx,&dx);  
}


void NMEA_GNRMC_Analysis(nmea_msg *gpsx,u8 *buf)
{
	u8 *p1,dx;			 
	u8 posx;     
	u32 temp;	   
	float rs;  
	p1=(u8*)strstr((const char *)buf,"$GNRMC");//"$GNRMC",经常有&和GNRMC分开的情况,故只判断GPRMC.
	posx=NMEA_Comma_Pos(p1,1);								//得到UTC时间
	if(posx!=0XFF)
	{
		temp=NMEA_Str2num(p1+posx,&dx)/NMEA_Pow(10,dx);	 	//得到UTC时间,去掉ms
		gpsx->utc.hour=temp/10000;
		gpsx->utc.min=(temp/100)%100;
		gpsx->utc.sec=temp%100;	 	 
	}	
	posx=NMEA_Comma_Pos(p1,3);								//得到纬度
	if(posx!=0XFF)
	{
		temp=NMEA_Str2num(p1+posx,&dx);		 	 
		gpsx->latitude=temp/NMEA_Pow(10,dx+2);	//得到°  mmnn.nnnnn
		rs=temp%NMEA_Pow(10,dx+2);				//得到'		 
		gpsx->latitude=gpsx->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° 
	}
	posx=NMEA_Comma_Pos(p1,4);								//南纬还是北纬 
	if(posx!=0XFF)gpsx->nshemi=*(p1+posx);					 
 	posx=NMEA_Comma_Pos(p1,5);								//得到经度
	if(posx!=0XFF)
	{												  
		temp=NMEA_Str2num(p1+posx,&dx);		 	 
		gpsx->longitude=temp/NMEA_Pow(10,dx+2);	//得到°
		rs=temp%NMEA_Pow(10,dx+2);				//得到'		 
		gpsx->longitude=gpsx->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° 
	}
	posx=NMEA_Comma_Pos(p1,6);								//东经还是西经
	if(posx!=0XFF)gpsx->ewhemi=*(p1+posx);		 
	posx=NMEA_Comma_Pos(p1,9);								//得到UTC日期
	if(posx!=0XFF)
	{
		temp=NMEA_Str2num(p1+posx,&dx);		 				//得到UTC日期
		gpsx->utc.date=temp/10000;
		gpsx->utc.month=(temp/100)%100;
		gpsx->utc.year=2000+temp%100;	 	 
	} 
}


void NMEA_GNVTG_Analysis(nmea_msg *gpsx,u8 *buf)
{
	u8 *p1,dx;			 
	u8 posx;    
	p1=(u8*)strstr((const char *)buf,"$GNVTG");							 
	posx=NMEA_Comma_Pos(p1,7);								//得到地面速率
	if(posx!=0XFF)
	{
		gpsx->speed=NMEA_Str2num(p1+posx,&dx);
		if(dx<3)gpsx->speed*=NMEA_Pow(10,3-dx);	 	 		//确保扩大1000倍
	}
}

7、气压计数据解析 

u32 bmp280CompensateT(s32 adcT)
{
    s32 var1, var2, T;

    var1 = ((((adcT >> 3) - ((s32)bmp280Cal.dig_T1 << 1))) * ((s32)bmp280Cal.dig_T2)) >> 11;
    var2  = (((((adcT >> 4) - ((s32)bmp280Cal.dig_T1)) * ((adcT >> 4) - ((s32)bmp280Cal.dig_T1))) >> 12) * ((s32)bmp280Cal.dig_T3)) >> 14;
    bmp280Cal.t_fine = var1 + var2;
	
    T = (bmp280Cal.t_fine * 5 + 128) >> 8;

    return T;
}

u32 bmp280CompensateP(s32 adcP)
{
    int64_t var1, var2, p;
    var1 = ((int64_t)bmp280Cal.t_fine) - 128000;
    var2 = var1 * var1 * (int64_t)bmp280Cal.dig_P6;
    var2 = var2 + ((var1*(int64_t)bmp280Cal.dig_P5) << 17);
    var2 = var2 + (((int64_t)bmp280Cal.dig_P4) << 35);
    var1 = ((var1 * var1 * (int64_t)bmp280Cal.dig_P3) >> 8) + ((var1 * (int64_t)bmp280Cal.dig_P2) << 12);
    var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)bmp280Cal.dig_P1) >> 33;
    if (var1 == 0)
        return 0;
    p = 1048576 - adcP;
    p = (((p << 31) - var2) * 3125) / var1;
    var1 = (((int64_t)bmp280Cal.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
    var2 = (((int64_t)bmp280Cal.dig_P8) * p) >> 19;
    p = ((p + var1 + var2) >> 8) + (((int64_t)bmp280Cal.dig_P7) << 4);
    return (uint32_t)p;
}

float bmp280PressureToAltitude(float pressure/*, float* groundPressure, float* groundTemp*/)
{	
    if(pressure > 0)
    {
		return 44330.f * (powf((1015.7f / pressure), 0.190295f) - 1.0f);
    }
    else
    {
        return 0;
    }
}

8、SBUS数据解析

u8 update_sbus(u8 *buf)
{
    int i;
    if (buf[23] == 0)
    {
        SBUS_CH.ConnectState = 1;
        SBUS_CH.CH1 = ((int16_t)buf[ 1] >> 0 | ((int16_t)buf[ 2] << 8 )) & 0x07FF;
        SBUS_CH.CH2 = ((int16_t)buf[ 2] >> 3 | ((int16_t)buf[ 3] << 5 )) & 0x07FF;
        SBUS_CH.CH3 = ((int16_t)buf[ 3] >> 6 | ((int16_t)buf[ 4] << 2 ) | (int16_t)buf[ 5] << 10 ) & 0x07FF;
        SBUS_CH.CH4 = ((int16_t)buf[ 5] >> 1 | ((int16_t)buf[ 6] << 7 )) & 0x07FF;
        SBUS_CH.CH5 = ((int16_t)buf[ 6] >> 4 | ((int16_t)buf[ 7] << 4 )) & 0x07FF;
        SBUS_CH.CH6 = ((int16_t)buf[ 7] >> 7 | ((int16_t)buf[ 8] << 1 ) | (int16_t)buf[9] << 9 ) & 0x07FF;
        SBUS_CH.CH7 = ((int16_t)buf[ 9] >> 2 | ((int16_t)buf[10] << 6 )) & 0x07FF;
        SBUS_CH.CH8 = ((int16_t)buf[10] >> 5 | ((int16_t)buf[11] << 3 )) & 0x07FF;
        SBUS_CH.CH9 = ((int16_t)buf[12] << 0 | ((int16_t)buf[13] << 8 )) & 0x07FF;
        SBUS_CH.CH10 = ((int16_t)buf[13] >> 3 | ((int16_t)buf[14] << 5 )) & 0x07FF;
        SBUS_CH.CH11 = ((int16_t)buf[14] >> 6 | ((int16_t)buf[15] << 2 ) | (int16_t)buf[16] << 10 ) & 0x07FF;
        SBUS_CH.CH12 = ((int16_t)buf[16] >> 1 | ((int16_t)buf[17] << 7 )) & 0x07FF;
        SBUS_CH.CH13 = ((int16_t)buf[17] >> 4 | ((int16_t)buf[18] << 4 )) & 0x07FF;
        SBUS_CH.CH14 = ((int16_t)buf[18] >> 7 | ((int16_t)buf[19] << 1 ) | (int16_t)buf[20] << 9 ) & 0x07FF;
        SBUS_CH.CH15 = ((int16_t)buf[20] >> 2 | ((int16_t)buf[21] << 6 )) & 0x07FF;
        SBUS_CH.CH16 = ((int16_t)buf[21] >> 5 | ((int16_t)buf[22] << 3 )) & 0x07FF;
        return 1;
    }
    else 
    {
        SBUS_CH.ConnectState = 0;
        return 0;
    }
}

u16 sbus_to_pwm(u16 sbus_value)
{
    float pwm;
    pwm = (float)SBUS_TARGET_MIN + (float)(sbus_value - SBUS_RANGE_MIN) * SBUS_SCALE_FACTOR;
    if (pwm > 2000) pwm = 2000;
    if (pwm < 1000) pwm = 1000;
    return (u16)pwm;
}

9、超声波数据解析

#define MAX_SPEED  40.0f
#define MIN_SPEED -40.0f

char US100_RxBuffer[2];

u8 US100_ENABLE_CMD_TBL[1]= {0x55};

extern ThrotMode ThrottleMode;

struct Z_Axis_Vector Z_Axis;

void Get_US100_Value(void)
{
	float temp;
	
	US100_Send_Cmd(US100_ENABLE_CMD_TBL,1);
	
	Src_Hight=US100_RxBuffer[0]*256+US100_RxBuffer[1];
	if( Src_Hight<0.0f || Src_Hight>4000.0f )
	{
		return;
	}
	SortAver_Filter_Multiple(&SortAverFilter_Hight,Src_Hight,&SortAverFilterHight,5,1);
	FirstOrderLowPassFilter(&LowPassFilter_Hight,SortAverFilterHight,0.76f,&LowPassFilterHight); 
	Z_Axis.CurrentHight=(u16)LowPassFilterHight;
	temp=(float)(Z_Axis.CurrentHight-Z_Axis.PreHight)/Z_Axis.T;  //单位mm/ms
	Z_Axis.PreHight=Z_Axis.CurrentHight;
		
	if(ThrottleMode == ThrotUp  &&  temp>0)
	{
		Z_Axis.Speed=temp;
	}
	else if(ThrottleMode == ThrotDown && temp<=0)
	{
		Z_Axis.Speed=temp*Z_Axis.Amplify;
	}
	else
	{
		Z_Axis.Speed=0;
	}
	
	Z_Axis.Speed = (Z_Axis.Speed>=MAX_SPEED) ? MAX_SPEED : Z_Axis.Speed;
	Z_Axis.Speed = (Z_Axis.Speed<=MIN_SPEED) ? MIN_SPEED : Z_Axis.Speed;

	if( fabs(Z_Axis.Speed)>8.0f )
	{
		LED0=~LED0;
	}
}

五、总结 

        创作不易,希望大家多多支持,感兴趣的朋友可以扫码进群,互相交流,共同进步。

STM32无人机/导航小车学习交流QQ群

 

### 关于毫米波雷达与单片机无人机中的应用 #### 毫米波雷达简介 毫米波雷达是一种利用极高频电磁波来检测物体距离、速度和角度的技术。其工作频率通常位于24GHz至77GHz之间,在无人机领域主要用于避障、定高以及目标跟踪等功能。 #### 单片机的作用 单片机作为控制核心,负责接收来自传感器的数据并执行相应的算法处理。对于搭载毫米波雷达系统的无人机而言,单片机会解析由雷达模块传来的信号,并据此调整飞行姿态或规划路径[^1]。 #### 应用场景描述 - **自动避障**:通过安装于机身周的多个小型化毫米波雷达单元实时监测周围环境变化;一旦探测到前方存在障碍物,则立即向MCU发送警报信息以便及时采取规避措施。 - **精准悬停**:借助底部朝下的窄束天线阵列持续测量地面反射回波强度差异从而计算高度差值反馈给控制系统用于维持稳定的高度位置不变。 - **跟随模式**:当配备有特定标识的人体或其他移动对象进入视野范围内时,能够锁定该目标并通过不断更新两者间的相对坐标关系保持适当的安全间距随行而动。 #### 实现方案概述 为了实现上述功能,可以采用如下架构设计: 1. 选用合适的毫米波雷达芯片组(如TI公司的IWRxx系列),它们具备较高的分辨率及较远的有效作用范围; 2. 配置高性能微控制器STM32F4/F7/L4等型号作为主处理器平台,确保足够的运算能力和丰富的外设接口资源满足多任务并发需求; 3. 编程方面推荐使用C/C++语言编写底层驱动层代码完成初始化配置参数设定等工作流程的同时还需移植FreeRTOS操作系统以提高软件健壮性和可维护性水平; 4. 对采集回来的目标数据进行滤波降噪预处理之后再送入卡尔曼滤波器做进一步优化估计最终得出精确可靠的决策依据供上位机调用显示或者直接参与闭环调节过程之中去影响实际物理行为动作表现形式。 ```c++ // 示例:简单的SPI通信函数声明 void SPI_Init(void); uint8_t SPI_TransmitReceive(uint8_t data); int main() { // 初始化SPI总线和其他必要的设置... while (true) { uint8_t radarData; // 发送命令读取雷达数据 SPI_TransmitReceive(READ_COMMAND); // 接收返回的数据包 radarData = SPI_TransmitReceive(DUMMY_BYTE); // 处理接收到的数据... } } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值