平衡小车代码部分全概括

注:本文代码全部来自于B站天下行走UP主,我只是加了很少的注释和自己的理解,他的这个视频我看了不下4、5遍,觉的讲的很好,点此转到B站 已征得本人同意!

初始化

编码器初始化(encoder.c)

编码器1——PA0/PA1—TIM2
编码器2——PB6/PB7—TIM4

void Encoder_TIM2_Init(void)
{
     
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_ICInitTypeDef TIM_ICInitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//  第一步:开启时钟,因为所有GPIO都挂载在APB2上,先开APB2
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //第五步:TIM2是通用定时器,挂载在APB1上。开启APB1
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;//第二步:初始化GPIO--PA0、PA1 因为用的是编码器模式,是读取的并不是输出的,                          	                                             所以不用配置Speed,使用浮空输入模式
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1; // PA0、PA1
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);      //第四步:  编码器模式需要用到定时器 	TIM_TimeBaseStructInit()定 时器初始化函数
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;       //时钟分割 设置不分频TIM_CKD_DIV1
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;   //模式 向上计数
	TIM_TimeBaseInitStruct.TIM_Period=65535;                     //重装载值
	TIM_TimeBaseInitStruct.TIM_Prescaler=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	


	TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);//编码器配置函数
	//函数有四个入口参数,定时器,编码器模式、极性
	TIM_ICStructInit(&TIM_ICInitStruct);//第三步:初始化输入捕获(编码器模式相关函数)
	TIM_ICInitStruct.TIM_ICFilter=10;    //滤波器 经验值     只配置这一个函数就可以 
	TIM_ICInit(TIM2,&TIM_ICInitStruct);
	
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);    // 清楚中断标志位,溢出更新
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);// 配置溢出更新中断标志
	TIM_SetCounter(TIM2,0);//    清零定时器计数值
	
	TIM_Cmd(TIM2,ENABLE);//开启定时器
}

编码器2——PB6/PB7—TIM4 代码和上面一样,改一下引脚和定时器就可以了

编码器初始化完成,下面写编码器的读取函数

先采集,再清零。那么采集到的数值就是速度

int Read_Speed(int TIMx)  //传入定时器X,读取TIM x的值
{
     
	int value_1;
	switch(TIMx)
	{
     
		case 2:value_1=(short)TIM_GetCounter(TIM2);TIM_SetCounter(TIM2,0);break;//IF是定时器2,1.采集编码器的计数值并保存。2.将定时器的计数值清零。	(如果是定时器2,就读取定时器2的计数值,保存到value_1;再把定时器2清零)
		case 4:value_1=(short)TIM_GetCounter(TIM4);TIM_SetCounter(TIM4,0);break;  //TIM_GetCounter()返回的是short类型,所以要强转一下,否则可能会出现一些小错误
		default:value_1=0;
	}
	return value_1;
}

中断寻址函数

void TIM2_IRQHandler(void)
{
     
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=0) //中断标志位不等于0,说明已经进入中断了,需要清除一下
	{
     
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}
	
void TIM4_IRQHandler(void)
{
     
	if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=0)
	{
     
		TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
	}
}

PWM(pwm.c)

PWM1——PA8
PWM2——PA11

void PWM_Init_TIM1(u16 Psc,u16 Per)   //传入两个参数,调控PWM
{
     
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_OCInitTypeDef TIM_OCInitStruct; 
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1 | RCC_APB2Periph_AFIO,ENABLE);//开启时钟
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//初始化GPIO--PA8、PA11为复用推挽输出 
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 |GPIO_Pin_11;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);//初始化定时器。
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period=Per;
	TIM_TimeBaseInitStruct.TIM_Prescaler=Psc;
	TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
	
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//初始化输出比较函数  模式 PWM1模式
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;         //极性  高
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;     //状态 
	TIM_OCInitStruct.TIM_Pulse=0;
	TIM_OC1Init(TIM1,&TIM_OCInitStruct);    //通道1
	TIM_OC4Init(TIM1,&TIM_OCInitStruct);    //通道4
	
	TIM_CtrlPWMOutputs(TIM1,ENABLE);//高级定时器专属--MOE主输出使能    一定要开!
	
	TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);/*【3】*///ENABLE//OC1预装载寄存器使能
	TIM_OC4PreloadConfig(TIM1,TIM_OCPreload_Enable);//ENABLE//OC4预装载寄存器使能
	TIM_ARRPreloadConfig(TIM1,ENABLE);//TIM1在ARR上预装载寄存器使能
	
	TIM_Cmd(TIM1,ENABLE);//开定时器。
}

EXTI MPU6050中断引脚——PB5

void MPU6050_EXTI_Init(void)
{
EXTI_InitTypeDef EXTI_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);//开启时钟   复用时钟	

GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;/**【1】//配置为上拉输入,因为mpu6050产生中断是有一个下降沿的
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//PB5配置为上拉输入
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);	

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5);//  中断映射到GPIO

EXTI_InitStruct.EXTI_Line=EXTI_Line5;    //中断线
EXTI_InitStruct.EXTI_LineCmd=ENABLE;  //使能
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;  //模式  中断触发
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;  //
EXTI_Init(&EXTI_InitStruct);

}

电机初始化(moter.c)

电机1——PB12/PB13
电机2——PB14/PB15



/*电机初始化函数*/
void Motor_Init(void)
{
     
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启时钟
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//初始化GPIO--PB12、PB13、PB14、PB15为推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12 |GPIO_Pin_13 |GPIO_Pin_14 |GPIO_Pin_15;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);	
}
int PWM_MAX=7200,PWM_MIN=-7200;	// 电机是10kHZ,如果分频是1,重装载值是7199,加1也就是7200,时钟是72M,(这里没听懂)
/*限幅函数*/
void Limit(int *motoA,int *motoB)
{
     
	if(*motoA>PWM_MAX)*motoA=PWM_MAX;
	if(*motoA<PWM_MIN)*motoA=PWM_MIN;
	
	if(*motoB>PWM_MAX)*motoB=PWM_MAX;
	if(*motoB<PWM_MIN)*motoB=PWM_MIN;
}

/*绝对值函数*/
int GFP_abs(int p)
{
     
	int q;
	q=p>0?p:(-p);
	return q;
}

/*赋值函数*/
/*入口参数:PID运算完成后的最终PWM值*/
void Load(int moto1,int moto2)//moto1=-200:反转200个脉冲
{
     
	//1.研究正负号,对应正反转
	if(moto1>0)	Ain1=1,Ain2=0;//正转
	else 				Ain1=0,Ain2=1;//反转
	//2.研究PWM值
	TIM_SetCompare1(TIM1,GFP_abs(moto1));  //通道1
	
	if(moto2>0)	Bin1=1,Bin2=0;
	else 				Bin1=0,Bin2=1;	
	TIM_SetCompare4(TIM1,GFP_abs(moto2));   //通道4
}

控制函数PID

PID理论部分我就不用CV大法了,PID
阿婆主在知乎上的文章已经很详细了

PID控制,就是对偏差进行比例、积分和微分的控制。
PID由3个单元组成,分别是比例(P)单元、积分(I)单元、微分(D)单位。
工程中P必然存在,在P的基础上又有如PD控制器、PI控制器、PID控制器等。

公式:Pwm=Kpe(k)+Ki∑e(k)+Kd*[e(k)-e(k-1)]

比例项=系数 乘以 偏差
积分项 =积分时间常数 乘以 偏差从0到k的积分 (由于计算机里没有积分,所以代码用的累加,积分就是无数个小部分的和)
微分项=系数x(本次偏差-上次偏差)/单位时间
他的这个图片就很棒

9–15更,要做车了,这个先放一放。先把代码贴下来,仅供参考!

control.c

#include "control.h"


float Med_Angle=0;//机械中值--能使得小车真正平衡住的角度。
float
	Vertical_Kp=-198,	//直立环KP、KD
	Vertical_Kd=-0.48;
float
	Velocity_Kp=-0.5,	//速度环KP、KI
	Velocity_Ki=-0.0025;
float 
	Turn_Kp=-0.8;

int Vertical_out,Velocity_out,Turn_out;//直立环&速度环&转向环 的输出变量

int Vertical(float Med,float Angle,float gyro_Y);//函数声明
int Velocity(int encoder_left,int encoder_right);
int Turn(int gyro_Z);

void EXTI9_5_IRQHandler(void)
{
     
	if(EXTI_GetITStatus(EXTI_Line5)!=0)//一级判定
	{
     
		int PWM_out;
		if(PBin(5)==0)//二级判定
		{
     
			EXTI_ClearITPendingBit(EXTI_Line5);//清除中断标志位
			
			//1.采集编码器数据&MPU6050角度信息。
			Encoder_Left=-Read_Speed(2);//电机是相对安装,刚好相差180度,为了编码器输出极性一致,就需要对其中一个取反。
			Encoder_Right=Read_Speed(4);
			
			mpu_dmp_get_data(&Pitch,&Roll,&Yaw);			//角度
			MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz);	//陀螺仪
			MPU_Get_Accelerometer(&aacx,&aacy,&aacz);	//加速度
			//2.将数据压入闭环控制中,计算出控制输出量。
			Vertical_out=Vertical(Med_Angle,Pitch,gyroy);				//直立环
			Velocity_out=Velocity(Encoder_Left,Encoder_Right);	//速度环
			Turn_out=Turn(gyroz);																//转向环
			
			PWM_out=Vertical_out-Vertical_Kp*Velocity_out;//最终输出
			//3.把控制输出量加载到电机上,完成最终的的控制。
			MOTO1=PWM_out-Turn_out;//左电机
			MOTO2=PWM_out+Turn_out;//右电机
			Limit(&MOTO1,&MOTO2);//PWM限幅			
			Load(MOTO1,MOTO2);//加载到电机上。
		}
	}
}




/*********************
直立环PD控制器:Kp*Ek+Kd*Ek_D

入口:期望角度、真实角度、真实角速度
出口:直立环输出
*********************/
int Vertical(float Med,float Angle,float gyro_Y)
{
     
	int PWM_out;
	
	PWM_out=Vertical_Kp*Angle+Vertical_Kd*(gyro_Y-0);//【1】
	return PWM_out;
}



/*********************
速度环PI:Kp*Ek+Ki*Ek_S
*********************/
int Velocity(int encoder_left,int encoder_right)
{
     
	static int PWM_out,Encoder_Err,Encoder_S,EnC_Err_Lowout,EnC_Err_Lowout_last;//【2】
	float a=0.7;//【3】
	
	//1.计算速度偏差
	Encoder_Err=(encoder_left+encoder_right)-0;//舍去误差
	//2.对速度偏差进行低通滤波
	//low_out=(1-a)*Ek+a*low_out_last;
	EnC_Err_Lowout=(1-a)*Encoder_Err+a*EnC_Err_Lowout_last;//使得波形更加平滑,滤除高频干扰,防止速度突变。
	EnC_Err_Lowout_last=EnC_Err_Lowout;//防止速度过大的影响直立环的正常工作。
	//3.对速度偏差积分,积分出位移
	Encoder_S+=EnC_Err_Lowout;//【4】
	//4.积分限幅
	Encoder_S=Encoder_S>10000?10000:(Encoder_S<(-10000)?(-10000):Encoder_S);
	//5.速度环控制输出计算
	PWM_out=Velocity_Kp*EnC_Err_Lowout+Velocity_Ki*Encoder_S;//【5】
	return PWM_out;
}



/*********************
转向环:系数*Z轴角速度
*********************/
int Turn(int gyro_Z)
{
     
	int PWM_out;
	
	PWM_out=Turn_Kp*gyro_Z;
	return PWM_out;
}


MPUIIC.c

#include "mpuiic.h"
#include "delay.h"

//MPU IIC 延时函数
void MPU_IIC_Delay(void)
{
     
	delay_us(2);
}

//初始化IIC
void MPU_IIC_Init(void)
{
     			
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;	//端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     //50M
  GPIO_Init(GPIOB, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB 
}
//产生IIC起始信号
void MPU_IIC_Start(void)
{
     
	MPU_SDA_OUT();     //sda线输出
	MPU_IIC_SDA=1;	  	  
	MPU_IIC_SCL=1;
	MPU_IIC_Delay();
 	MPU_IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	MPU_IIC_Delay();
	MPU_IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void MPU_IIC_Stop(void)
{
     
	MPU_SDA_OUT();//sda线输出
	MPU_IIC_SCL=0;
	MPU_IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	MPU_IIC_Delay();
	MPU_IIC_SCL=1;  
	MPU_IIC_SDA=1;//发送I2C总线结束信号
	MPU_IIC_Delay();							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 MPU_IIC_Wait_Ack(void)
{
     
	u8 ucErrTime=0;
	MPU_SDA_IN();      //SDA设置为输入  
	MPU_IIC_SDA=1;MPU_IIC_Delay();	   
	MPU_IIC_SCL=1;MPU_IIC_Delay();	 
	while(MPU_READ_SDA)
	{
     
		ucErrTime++;
		if(ucErrTime>250)
		{
     
			MPU_IIC_Stop();
			return 1;
		}
	}
	MPU_IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void MPU_IIC_Ack(void)
{
     
	MPU_IIC_SCL=0;
	MPU_SDA_OUT();
	MPU_IIC_SDA=0;
	MPU_IIC_Delay();
	MPU_IIC_SCL=1;
	MPU_IIC_Delay();
	MPU_IIC_SCL=0;
}
//不产生ACK应答		    
void MPU_IIC_NAck(void)
{
     
	MPU_IIC_SCL=0;
	MPU_SDA_OUT();
	MPU_IIC_SDA=1;
	MPU_IIC_Delay();
	MPU_IIC_SCL=1;
	MPU_IIC_Delay();
	MPU_IIC_SCL=0;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void MPU_IIC_Send_Byte(u8 txd)
{
                             
    u8 t;   
	MPU_SDA_OUT(); 	    
    MPU_IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {
                   
        MPU_IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		MPU_IIC_SCL=1;
		MPU_IIC_Delay(); 
		MPU_IIC_SCL=0;	
		MPU_IIC_Delay();
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 MPU_IIC_Read_Byte(unsigned char ack)
{
     
	unsigned char i,receive=0;
	MPU_SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
     
        MPU_IIC_SCL=0; 
        MPU_IIC_Delay();
		MPU_IIC_SCL=1;
        receive<<=1;
        if(MPU_READ_SDA)receive++;   
		MPU_IIC_Delay(); 
    }					 
    if (!ack)
        MPU_IIC_NAck();//发送nACK
    else
        MPU_IIC_Ack(); //发送ACK   
    return receive;
}



MPU6050.C

#include "mpu6050.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"   



//初始化MPU6050
//返回值:0,成功
//其他,错误代码
u8 MPU_Init(void)
{
      
	u8 res; 
	MPU_IIC_Init();//初始化IIC总线
	MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80);	//复位MPU6050
    delay_ms(100);
	MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00);	//唤醒MPU6050 
	MPU_Set_Gyro_Fsr(3);					//陀螺仪传感器,±2000dps
	MPU_Set_Accel_Fsr(0);					//加速度传感器,±2g
	MPU_Set_Rate(200);						//设置采样率50Hz
	MPU_Write_Byte(MPU_INT_EN_REG,0X00);	//关闭所有中断
	MPU_Write_Byte(MPU_USER_CTRL_REG,0X00);	//I2C主模式关闭
	MPU_Write_Byte(MPU_FIFO_EN_REG,0X00);	//关闭FIFO
	MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80);	//INT引脚低电平有效
	res=MPU_Read_Byte(MPU_DEVICE_ID_REG); 
	if(res==MPU_ADDR)//器件ID正确
	{
     
		MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01);	//设置CLKSEL,PLL X轴为参考
		MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00);	//加速度与陀螺仪都工作
		MPU_Set_Rate(100);						//设置采样率为50Hz
 	}else return 1;
	return 0;
}
//设置MPU6050陀螺仪传感器满量程范围
//fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
//返回值:0,设置成功
//    其他,设置失败 
u8 MPU_Set_Gyro_Fsr(u8 fsr)
{
     
	return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);//设置陀螺仪满量程范围  
}
//设置MPU6050加速度传感器满量程范围
//fsr:0,±2g;1,±4g;2,±8g;3,±16g
//返回值:0,设置成功
//    其他,设置失败 
u8 MPU_Set_Accel_Fsr(u8 fsr)
{
     
	return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);//设置加速度传感器满量程范围  
}
//设置MPU6050的数字低通滤波器
//lpf:数字低通滤波频率(Hz)
//返回值:0,设置成功
//    其他,设置失败 
u8 MPU_Set_LPF(u16 lpf)
{
     
	u8 data=0;
	if(lpf>=188)data=1;
	else if(lpf>=98)data=2;
	else if(lpf>=42)data=3;
	else if(lpf>=20)data=4;
	else if(lpf>=10)data=5;
	else data=6; 
	return MPU_Write_Byte(MPU_CFG_REG,data);//设置数字低通滤波器  
}
//设置MPU6050的采样率(假定Fs=1KHz)
//rate:4~1000(Hz)
//返回值:0,设置成功
//    其他,设置失败 
u8 MPU_Set_Rate(u16 rate)
{
     
	u8 data;
	if(rate>1000)rate=1000;
	if(rate<4)rate=4;
	data=1000/rate-1;
	data=MPU_Write_Byte(MPU_SAMPLE_RATE_REG,data);	//设置数字低通滤波器
 	return MPU_Set_LPF(rate/2);	//自动设置LPF为采样率的一半
}

//得到温度值
//返回值:温度值(扩大了100倍)
short MPU_Get_Temperature(void)
{
     
    u8 buf[2]; 
    short raw;
	float temp;
	MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf); 
    raw=((u16)buf[0]<<8)|buf[1];  
    temp=36.53+((double)raw)/340;  
    return temp*100;;
}
//得到陀螺仪值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
//    其他,错误代码
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
     
    u8 buf[6],res;  
	res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);
	if(res==0)
	{
     
		*gx=((u16)buf[0]<<8)|buf[1];  
		*gy=((u16)buf[2]<<8)|buf[3];  
		*gz=((u16)buf[4]<<8)|buf[5];
	} 	
    return res;;
}
//得到加速度值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
//    其他,错误代码
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{
     
    u8 buf[6],res;  
	res=MPU_Read_Len(MPU_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);
	if(res==0)
	{
     
		*ax=((u16)buf[0]<<8)|buf[1];  
		*ay=((u16)buf[2]<<8)|buf[3];  
		*az=((u16)buf[4]<<8)|buf[5];
	} 	
    return res;;
}
//IIC连续写
//addr:器件地址 
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
//    其他,错误代码
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
     
	u8 i; 
    MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令	
	if(MPU_IIC_Wait_Ack())	//等待应答
	{
     
		MPU_IIC_Stop();		 
		return 1;		
	}
    MPU_IIC_Send_Byte(reg);	//写寄存器地址
    MPU_IIC_Wait_Ack();		//等待应答
	for(i=0;i<len;i++)
	{
     
		MPU_IIC_Send_Byte(buf[i]);	//发送数据
		if(MPU_IIC_Wait_Ack())		//等待ACK
		{
     
			MPU_IIC_Stop();	 
			return 1;		 
		}		
	}    
    MPU_IIC_Stop();	 
	return 0;	
} 
//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
//    其他,错误代码
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
      
 	MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令	
	if(MPU_IIC_Wait_Ack())	//等待应答
	{
     
		MPU_IIC_Stop();		 
		return 1;		
	}
    MPU_IIC_Send_Byte(reg);	//写寄存器地址
    MPU_IIC_Wait_Ack();		//等待应答
    MPU_IIC_Start();
	MPU_IIC_Send_Byte((addr<<1)|1);//发送器件地址+读命令	
    MPU_IIC_Wait_Ack();		//等待应答 
	while(len)
	{
     
		if(len==1)*buf=MPU_IIC_Read_Byte(0);//读数据,发送nACK 
		else *buf=MPU_IIC_Read_Byte(1);		//读数据,发送ACK  
		len--;
		buf++; 
	}    
    MPU_IIC_Stop();	//产生一个停止条件 
	return 0;	
}
//IIC写一个字节 
//reg:寄存器地址
//data:数据
//返回值:0,正常
//    其他,错误代码
u8 MPU_Write_Byte(u8 reg,u8 data) 				 
{
      
    MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令	
	if(MPU_IIC_Wait_Ack())	//等待应答
	{
     
		MPU_IIC_Stop();		 
		return 1;		
	}
    MPU_IIC_Send_Byte(reg);	//写寄存器地址
    MPU_IIC_Wait_Ack();		//等待应答 
	MPU_IIC_Send_Byte(data);//发送数据
	if(MPU_IIC_Wait_Ack())	//等待ACK
	{
     
		MPU_IIC_Stop();	 
		return 1;		 
	}		 
    MPU_IIC_Stop();	 
	return 0;
}
//IIC读一个字节 
//reg:寄存器地址 
//返回值:读到的数据
u8 MPU_Read_Byte(u8 reg)
{
     
	u8 res;
    MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令	
	MPU_IIC_Wait_Ack();		//等待应答 
    MPU_IIC_Send_Byte(reg);	//写寄存器地址
    MPU_IIC_Wait_Ack();		//等待应答
    MPU_IIC_Start();
	MPU_IIC_Send_Byte((MPU_ADDR<<1)|1);//发送器件地址+读命令	
    MPU_IIC_Wait_Ack();		//等待应答 
	res=MPU_IIC_Read_Byte(0);//读取数据,发送nACK 
    MPU_IIC_Stop();			//产生一个停止条件 
	return res;		
}



你可能感兴趣的:(小白做平衡车全过程,stm32)