基于STM32C8T6的蓝牙PS4遥控小车手柄

文章目录

      • 项目背景
      • 手柄功能介绍(附代码讲解)
        • 蓝牙遥控四个模式
        • DS18B20测量温度
        • 获取电池ADC电压和PS4摇杆的XY轴ADC电压,采用ADC+DMA
        • 锂电池电源部分+USB充电
      • 总体电路原理图
      • 需要硬件PCB和软件代码工程+Q2877488930或者+微信Aaa2877488930
      • 演示视频在视频栏
      • 附录图片

项目背景

之前本人制作了一个智能小车,当时是用手机蓝牙APP对小车进行遥控,后来就想着用自己绘制一个遥控手柄,显得高端一点哈哈哈,所以参考了手柄制作要点,主要还是好看并且拿在手里舒适,就用CAD绘制了手柄的板框层,导入AD进行PCB绘制,PCB板框层和最后布局如下:
实物图
基于STM32C8T6的蓝牙PS4遥控小车手柄_第1张图片

PCB 3D图
基于STM32C8T6的蓝牙PS4遥控小车手柄_第2张图片
PCB布线图
基于STM32C8T6的蓝牙PS4遥控小车手柄_第3张图片
CAD 手柄外形图
基于STM32C8T6的蓝牙PS4遥控小车手柄_第4张图片

手柄功能介绍(附代码讲解)

蓝牙遥控四个模式

  • 小车运动模式
  • 小车红外循迹模式
  • 小车超声波避障模式
  • 小车跟随模式
    代码实现:使用HC05模块实现两个单片机之间的串口通信(当然使用ESP8266也可以),手柄发出串口指令给小车的单片机,小车就可以根据指令作出反应(串口2中断服务函数实现)
    小车端串口代码
void USART2_IRQHandler(void)                	//串口2中断服务程序
{	
	u8 res;  
     if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)  
 {  
     res= USART_ReceiveData(USART2); 	
        //LED1=!LED1; 
			 if(res=='a') flag='a';//前进
	else if(res=='b') flag='b';//后退
	else if(res=='c') flag='c';//左拐
	else if(res=='d') flag='d';//右拐
	else if(res=='e') flag='e';//停止
	
	else if(res=='f') flag='f';//速度低档
	else if(res=='g') flag='g';//速度快档
  else if(res=='h') flag='h'; //速度中档

	else if(res=='i') mode=1;//蓝牙遥控模式
	else if(res=='j') mode=2;//红外循迹模式
	else if(res=='l') mode=4;//红外跟随模式
	else if(res=='k')//避障跟随模式
	{
		mode=3;
		SR04_FLAG++;
		if(SR04_FLAG==3)
		{
		SR04_FLAG=1;
		}
		
	}
		
 }  

遥控端串口发送指令代码
说明:发送那个字符通过操作按键和PS4摇杆决定,比如发送字符’a’就是前进指令,其他依此类推

if((x==1&&y==3)||(x==1&&y==2))//前进
			{
				USART_SendData(USART1,'a');
			OLED_ShowCHinese(80,4,27);
	  	OLED_ShowCHinese(96,4,28);
			
			}
				if(x==1&&y==0)//后退
			{
				USART_SendData(USART1,'b');
			OLED_ShowCHinese(80,4,29);
	  	OLED_ShowCHinese(96,4,30);
			
			}
				if((x==3&&y==1)||(x==3&&y==0)||(x==3&&y==2))//左拐
			{
				USART_SendData(USART1,'c');
			OLED_ShowCHinese(80,4,33);
	  	OLED_ShowCHinese(96,4,34);
			
			}
							if((x==0&&y==1)||(x==0&&y==2)||(x==0&&y==3))//右拐
			{
				USART_SendData(USART1,'d');
			OLED_ShowCHinese(80,4,35);
	  	OLED_ShowCHinese(96,4,36);
			
			}
			
			if(x==1&&y==1)//停止
			{
				USART_SendData(USART1,'e');
			OLED_ShowCHinese(80,4,31);
	  	OLED_ShowCHinese(96,4,32);
			
			}
			//按键选择模式
       key=KEY_Scan(0);	//得到键值
			if(key)
		{						   
			switch(key)
			{				 
			
				
				case KEY4_PRES:	//速度档位模式
        flag0++;
				if(flag0==1)//低档
				{
				OLED_ShowCHinese(40,2,14);
					USART_SendData(USART1,'f');//发送字符f给小车,小车接收到指令就可以进行相关操作,以下类推
				   	Buzzer_ON();
					  delay_ms(100);
					  Buzzer_OFF();
				}
				else if(flag0==2)//中档
				{
				OLED_ShowCHinese(40,2,11);
				USART_SendData(USART1,'h');
					  Buzzer_ON();
					  delay_ms(100);
					  Buzzer_OFF();
				}
        else if(flag0==3)//快裆
				{
				OLED_ShowCHinese(40,2,13);
				USART_SendData(USART1,'g');
					flag0=0;
					  Buzzer_ON();
					  delay_ms(100);
					  Buzzer_OFF();
				}
				
				break;
				
				case KEY3_PRES:	//避障跟随模式
				{	
					
					flag++;//flag原理都是一个按键切换多种模式
					if(flag==3)
						{
							flag=1;
						}
					if(flag==1)
					{
		OLED_ShowCHinese(40,0,21);
		OLED_ShowCHinese(56,0,22);
		OLED_ShowCHinese(72,0,9);
		OLED_ShowCHinese(88,0,10);
	  USART_SendData(USART1,'k');
					}
					else if(flag==2)
					{
		OLED_ShowCHinese(40,0,17);
		OLED_ShowCHinese(56,0,18);
		OLED_ShowCHinese(72,0,9);
		OLED_ShowCHinese(88,0,10);
					}
				  USART_SendData(USART1,'k');
				    Buzzer_ON();
					  delay_ms(100);
					  Buzzer_OFF();
				}
					break;
				case KEY2_PRES:	//红外循迹模式
				{	
		OLED_ShowCHinese(40,0,2);
		OLED_ShowCHinese(56,0,3);
		OLED_ShowCHinese(72,0,4);
		OLED_ShowCHinese(88,0,5);
				  USART_SendData(USART1,'j');//
				    Buzzer_ON();
					  delay_ms(100);
					  Buzzer_OFF();
				}
					break;
				case KEY1_PRES:	//蓝牙遥控模式
				{	
		OLED_ShowCHinese(40,0,0);
		OLED_ShowCHinese(56,0,1);
		OLED_ShowCHinese(72,0,19);
		OLED_ShowCHinese(88,0,20);
				  USART_SendData(USART1,'i');//
				    Buzzer_ON();
					  delay_ms(100);
					  Buzzer_OFF();
				}
					break;
			}
		}

DS18B20测量温度

  • 这个功能是当手柄闲置没用时可以测量环境温度,不加这个也可
    代码实现:'参考正点原子的DS18B20实验即可,当然也可自己根据DS18B20的时序自己编写读写数据函数(对新手不友好)

ds18b.c:

#include "ds18b20.h"
#include "delay.h"	
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK MiniSTM32开发板
//DS18B20驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2014/4/12
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved									  
//
  

//复位DS18B20
void DS18B20_Rst(void)	   
{                 
	DS18B20_IO_OUT(); //SET PA0 OUTPUT
    DS18B20_DQ_OUT=0; //拉低DQ
    delay_us(750);    //拉低750us
    DS18B20_DQ_OUT=1; //DQ=1 
	delay_us(15);     //15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void) 	   
{   
	u8 retry=0;
	DS18B20_IO_IN();//SET PA0 INPUT	 
    while (DS18B20_DQ_IN&&retry<200)
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=200)return 1;
	else retry=0;
    while (!DS18B20_DQ_IN&&retry<240)
	{
		retry++;
		delay_us(1);
	};
	if(retry>=240)return 1;	    
	return 0;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void) 			 // read one bit
{
    u8 data;
	DS18B20_IO_OUT();//SET PA0 OUTPUT
    DS18B20_DQ_OUT=0; 
	delay_us(2);
    DS18B20_DQ_OUT=1; 
	DS18B20_IO_IN();//SET PA0 INPUT
	delay_us(12);
	if(DS18B20_DQ_IN)data=1;
    else data=0;	 
    delay_us(50);           
    return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)    // read one byte
{        
    u8 i,j,dat;
    dat=0;
	for (i=1;i<=8;i++) 
	{
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
    }						    
    return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)     
 {             
    u8 j;
    u8 testb;
	DS18B20_IO_OUT();//SET PA0 OUTPUT;
    for (j=1;j<=8;j++) 
	{
        testb=dat&0x01;
        dat=dat>>1;
        if (testb) 
        {
            DS18B20_DQ_OUT=0;// Write 1
            delay_us(2);                            
            DS18B20_DQ_OUT=1;
            delay_us(60);             
        }
        else 
        {
            DS18B20_DQ_OUT=0;// Write 0
            delay_us(60);             
            DS18B20_DQ_OUT=1;
            delay_us(2);                          
        }
    }
}
//开始温度转换
void DS18B20_Start(void)// ds1820 start convert
{   						               
    DS18B20_Rst();	   
	DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);// skip rom
    DS18B20_Write_Byte(0x44);// convert
} 
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在    	 
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在    	 
u8 DS18B20_Init(void)
{
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PORTA口时钟 
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				//PORTB13 推挽输出
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);

 	GPIO_SetBits(GPIOB,GPIO_Pin_13);    //输出1

	DS18B20_Rst();

	return DS18B20_Check();
}  

void DS18B20_IO_IN()
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PORTA口时钟 
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				//PORTB13
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		  
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void DS18B20_IO_OUT()
{
  GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PORTA口时钟 
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				//PORTB13 推挽输出
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250) 
short DS18B20_Get_Temp(void)
{
    u8 temp;
    u8 TL,TH;
	short tem;
    DS18B20_Start ();                    // ds1820 start convert
    DS18B20_Rst();
    DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);// skip rom
    DS18B20_Write_Byte(0xbe);// convert	    
    TL=DS18B20_Read_Byte(); // LSB   
    TH=DS18B20_Read_Byte(); // MSB  
	    	  
    if(TH>7)
    {
        TH=~TH;
        TL=~TL; 
        temp=0;//温度为负  
    }else temp=1;//温度为正	  	  
    tem=TH; //获得高八位
    tem<<=8;    
    tem+=TL;//获得底八位
    tem=(float)tem*0.625;//转换     
	if(temp)return tem; //返回温度值
	else return -tem;    
}

ds18b20.h:

#ifndef __DS18B20_H
#define __DS18B20_H 
#include "sys.h"   
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK MiniSTM32开发板
//DS18B20驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2014/3/12
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved									  
//

//IO方向设置
//#define DS18B20_IO_IN()  {GPIOB->CRH&=0XFF0FFFFF;GPIOB->CRH|=8<<5;}
//#define DS18B20_IO_OUT() {GPIOB->CRH&=0XFF0FFFFF;GPIOB->CRH|=3<<5;}
void  DS18B20_IO_OUT(void);
void  DS18B20_IO_IN(void);
IO操作函数											   
#define	DS18B20_DQ_OUT PBout(13) //数据端口	PB13
#define	DS18B20_DQ_IN  PBin(13)  //数据端口	PB13
   	
u8 DS18B20_Init(void);			//初始化DS18B20
short DS18B20_Get_Temp(void);	//获取温度
void DS18B20_Start(void);		//开始温度转换
void DS18B20_Write_Byte(u8 dat);//写入一个字节
u8 DS18B20_Read_Byte(void);		//读出一个字节
u8 DS18B20_Read_Bit(void);		//读出一个位
u8 DS18B20_Check(void);			//检测是否存在DS18B20
void DS18B20_Rst(void);			//复位DS18B20    
#endif

主函数调用显示温度函数:

T=DS18B20_Get_Temp();//获取温度
					if(T<0)//判断温度正负
	{
	OLED_ShowChar(72,0,'-',16); 
	}
	else
	{
	OLED_ShowChar(72,0,'+',16); 
	}
	OLED_ShowCHinese(0,0,37);	
	OLED_ShowCHinese(16,0,38);	
	OLED_ShowCHinese(32,0,39);	
	OLED_ShowCHinese(48,0,40);
	OLED_ShowString(64,0,":",16); 
  OLED_ShowCHinese(110,0,41);
	OLED_ShowString(96,0,".",16); 
	OLED_ShowNum(101,0,T%10,1,16);
	OLED_ShowNum(80,0,T/10,2,16);

获取电池ADC电压和PS4摇杆的XY轴ADC电压,采用ADC+DMA

  • 获取ADC电源电压是可以随时可以看见电池电压,不用万用表去随时检。另外两个ADC就是读取PS4遥杆的两个输出口电压,往哪边拨动,两个ADC电压就会发生变化,根据变化情况就可以判别是往那边拨动的
    代码实现:由于是三个ADC,所以我们用DMA多通道采集ADC,然后将读取数据存放到长度为3的数组中,在主函数不断访问这个数组的值就可以读出ADC电压。这个数组的首地址作为DMA的基地址

adc.c:

 #include "adc.h"
 #include "delay.h"
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK miniSTM32开发板
//ADC 代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/7
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved									  
// 

extern volatile uint16_t ADC_ConveredValue[3]; 
		   
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3																	   
void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1	, ENABLE );	  //使能ADC1通道时钟
 

	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

	//PA1 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	
	ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;	//模数转换工作在单通道模式
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;	//模数转换工作在单次转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 3;	//顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC1, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   

  
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);        
	ADC_RegularChannelConfig(ADC1,ADC_Channel_2, 2, ADC_SampleTime_239Cycles5);	       
  ADC_RegularChannelConfig(ADC1,ADC_Channel_5, 3, ADC_SampleTime_239Cycles5);	       

//  ADC_DMACmd(ADC1, ENABLE);                  //使能ADC1的DMA功能
	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
	
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	
	ADC_StartCalibration(ADC1);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
 
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能
}				  
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)   
{
  	//设置指定ADC的规则组通道,一个序列,采样时间,1是转换顺序,多通道使用,单通道就没有顺序而言了,设置1即可
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  			    
  
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}

u16 Get_Adc_Average(u8 ch,u8 times)//正点原子求平均值测试,防止ADC数值波动
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=Get_Adc(ch);
		delay_ms(5);
	}
	return temp_val/times;
} 	 


u16 Get_Adc_Average2(u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=ADC_ConveredValue[2];//DMA电池电压
		delay_ms(5);
	}
	return temp_val/times;
} 	 
u16 Get_Adc_Average1(u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=ADC_ConveredValue[1];//DMA y轴电压
		delay_ms(5);
	}
	return temp_val/times;
} 	
u16 Get_Adc_Average0(u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=ADC_ConveredValue[0];//DMA x轴电压
		delay_ms(5);
	}
	return temp_val/times;
} 	

adc.h+DMA配置

  • adc.h
#ifndef __ADC_H
#define __ADC_H	
#include "sys.h"
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK战舰STM32开发板
//ADC 代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/7
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved									  
// 

void Adc_Init(void);
u16  Get_Adc(u8 ch); 
u16 Get_Adc_Average(u8 ch,u8 times); 
u16 Get_Adc_Average0(u8 times); 
u16 Get_Adc_Average1(u8 times); 
u16 Get_Adc_Average2(u8 times); 
#endif 
  • DMA
//ADCDMA配置
void ADC_DMA_Init(void)
{
  DMA_InitTypeDef DMA_InitStructure;

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

  DMA_DeInit(DMA1_Channel1);

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);  //ADC外设数据寄存器地址作为基地址
  DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)ADC_ConveredValue;//存储数据内存地址
  DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC; //传输方向为外设到内存
  DMA_InitStructure.DMA_BufferSize         = 3;		  //单位是下面的HalfWord 16bit
  DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;  //用到多通道ADC,                     所以使能自动增加,一个通道转换完的数据放在g_stTempInfo.ADC_ValTab[0],
                                                                   //下一个通道数据就自动存放在g_stTempInfo.ADC_ValTab[1]
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据大小为半字
  DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;   //内存数据大小也为半字
  DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;  //循环模式
  DMA_InitStructure.DMA_Priority           = DMA_Priority_High;   //优先级高
  DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;    //不使用内存到内存的传输
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  /* Enable DMA1 Channel1 */
  DMA_Cmd(DMA1_Channel1, ENABLE);		      //使能DMA通道1 
	ADC_DMACmd(ADC1, ENABLE);                  //使能ADC1的DMA功能
}  

主函数调用

  • 电池电压部分
OLED_ShowCHinese(0,4,42);	
	OLED_ShowCHinese(16,4,43);	
	OLED_ShowCHinese(32,4,44);	
	OLED_ShowCHinese(48,4,45);
	OLED_ShowString(64,4,":",16); 	
				
  adc=Get_Adc_Average2(20)*(3.3/4096)*1000;//获取电池电压
		
	OLED_ShowNum(96,4,adc%10,1,16);
	OLED_ShowString(80,4,".",16);
	OLED_ShowNum(88,4,adc/10%10,1,16);
	OLED_ShowNum(72,4,adc/100%10,1,16);
//			OLED_ShowNum(72,2,flag1,1,16);
	OLED_ShowString(104,4,"V",16);			
  • PS4遥感XY输出部分
x=(int)Get_Adc_Average0(10)*(3.3/4096);//PS4遥杆x轴ADC电压,取int型
			y=(int)Get_Adc_Average1(10)*(3.3/4096);//PS4遥杆y轴ADC电压
//	    OLED_ShowNum(120,4,x,1,16);
//			OLED_ShowNum(128,4,y,1,16);
			//以下根据PS4遥感输出ADC即可判断
			if((x==1&&y==3)||(x==1&&y==2))//前进
			{
				USART_SendData(USART1,'a');
			OLED_ShowCHinese(80,4,27);
	  	OLED_ShowCHinese(96,4,28);
			
			}
				if(x==1&&y==0)//后退
			{
				USART_SendData(USART1,'b');
			OLED_ShowCHinese(80,4,29);
	  	OLED_ShowCHinese(96,4,30);
			
			}
				if((x==3&&y==1)||(x==3&&y==0)||(x==3&&y==2))//左拐
			{
				USART_SendData(USART1,'c');
			OLED_ShowCHinese(80,4,33);
	  	OLED_ShowCHinese(96,4,34);
			
			}
							if((x==0&&y==1)||(x==0&&y==2)||(x==0&&y==3))//右拐
			{
				USART_SendData(USART1,'d');
			OLED_ShowCHinese(80,4,35);
	  	OLED_ShowCHinese(96,4,36);
			
			}
			
			if(x==1&&y==1)//停止
			{
				USART_SendData(USART1,'e');
			OLED_ShowCHinese(80,4,31);
	  	OLED_ShowCHinese(96,4,32);
			
			}

锂电池电源部分+USB充电

  • 遥控手柄本身不大,所以电池就选用了3.7V锂电池进行供电,3.7V经过3V3稳压芯片RT9193-3.3稳压稳定输出3V3电压给单片机和外设供电。
  • 此外为了手柄方便充电,手柄就采用了充电控制芯片TP4056和Mic-USB给锂电池进行充电,实际测试效果不错!
    下面是稳压电路原理图:
    基于STM32C8T6的蓝牙PS4遥控小车手柄_第5张图片
    下面是充电电路原理图:
    基于STM32C8T6的蓝牙PS4遥控小车手柄_第6张图片

总体电路原理图

基于STM32C8T6的蓝牙PS4遥控小车手柄_第7张图片

需要硬件PCB和软件代码工程+Q2877488930或者+微信Aaa2877488930

演示视频在视频栏

附录图片

基于STM32C8T6的蓝牙PS4遥控小车手柄_第8张图片

基于STM32C8T6的蓝牙PS4遥控小车手柄_第9张图片
基于STM32C8T6的蓝牙PS4遥控小车手柄_第10张图片
基于STM32C8T6的蓝牙PS4遥控小车手柄_第11张图片
基于STM32C8T6的蓝牙PS4遥控小车手柄_第12张图片

你可能感兴趣的:(stm32,单片机,arm)