(纯萌新,学习单片机半年了,这是寒假回家的作品,师从江科大,写博客纪录我实现后的经验)(比较粗略)
蓝牙主从模式遥杆控制小车,我将其分为了三部分:1.让小车动 2.蓝牙主从连接与信号的发送接收 3.获取摇杆的状态并控制小车转向
首先是让小车动起来
目标:内部电源,调节参数转向。
原理:让小车动只需上电即可。通过程序控制PWM的CCR而调节占空比,可以向电机输出频率大小不同的电流,可以调节小车的转动速度。当两个轮子转速不同时,小车就会转向。内部电源就是弄了电池和L298n,电池输出7-12v的电压,接在L298n电机驱动模块上可以输出5v电压,再转化为3.3v给单片机供电即可。我使用了淘宝商家的小车底盘和电路板,这个电路的唯一作用就是可以把5v转变为3.3v供电,此外也可以用电源模块在面包板上将5v转变为3.3v。
过程:对于我来说难在电源,通过两次转换让电源由两节3.7v转变为3.3v。
具体PWM代码和电机代码(大部分为江科大内容,没什么好说的)
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_0 ; //用的通道1PA0和通道2PA1
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitSturcture;
TIM_TimeBaseInitSturcture.TIM_ClockDivision= TIM_CKD_DIV1;
TIM_TimeBaseInitSturcture.TIM_CounterMode= TIM_CounterMode_Up;
TIM_TimeBaseInitSturcture.TIM_Period= 100-1; //ARR
TIM_TimeBaseInitSturcture.TIM_Prescaler= 720-1; //PSC 电机的参数
TIM_TimeBaseInitSturcture.TIM_RepetitionCounter= 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitSturcture);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode= TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse=0 ; //CCR
TIM_OC1Init(TIM2,&TIM_OCInitStructure);
TIM_OC2Init(TIM2,&TIM_OCInitStructure); //此处通道二PA1,所以用OC2,可以用多个通道控制多个舵机
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare); //通道二的SetCompare2
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2,Compare); //通道二的SetCompare2
}
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure; //方向GPIO口初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_4 | GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM_Init(); //初始化PWM
}
void Motor_TwoSetSpeed(int8_t Speed)
{
if(Speed>=0)
{
PWM_SetCompare1(Speed);
PWM_SetCompare2(Speed);
}
if(Speed<=0)
{
PWM_SetCompare1(-Speed);
PWM_SetCompare2(-Speed);
}
}
void Motor_L_SetSpeed(int8_t Speed)
{
if(Speed>=0)
{
PWM_SetCompare1(Speed);
}
if(Speed<=0)
{
PWM_SetCompare1(-Speed);
}
}
void Motor_R_SetSpeed(int8_t Speed)
{
if(Speed>=0)
{
PWM_SetCompare2(Speed);
}
if(Speed<=0)
{
PWM_SetCompare2(-Speed);
}
}
void Motor_Stop(void)
{
PWM_SetCompare1(0);
PWM_SetCompare2(0);
}
void Motor_Retreat()
{
GPIO_SetBits(GPIOA,GPIO_Pin_8);
GPIO_SetBits(GPIOA,GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
}
void Motor_Go()
{
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_8);
}
让两片蓝牙主从通信
目标:在实现主从模式前,我的第一目标是让手机能连上蓝牙并向小车发送信息
过程:蓝牙我使用的是HC-05,可配置主从,HC-05出厂为从模式。首先通过AT模式改变HC-05的波特率为9600即可。然后将HC-05连接在面包板上,使用usart2作为通信媒介,对照引脚定义表连接Rx,Tx,把接收数据的代码写好,通过手机app连接蓝牙发送即可。
下面是蓝牙的从模式代码(用到了中断)
#include "stm32f10x.h" // Device header
uint8_t LanYa_RxData;
uint8_t LanYa_RxFlag;
void LanYa_Init(void) //初始化
{
//第一步,开启时钟(APB1——USART2)(GPIO引脚)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //注意,USART1是APB2的外设,USART2是APB1的外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //打开GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //TX是输出脚用复用推挽,RX是输入脚用输入模式(不分模式)一般浮空
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_2; //TX
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU; //Tx是输出脚用复用推挽,RX是输入脚用输入模式(不分模式)一般浮空或上拉
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_3; //Rx
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//第二步,初始化USART(结构体)
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate= 9600; //波特率
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ; //流控使用
USART_InitStructure.USART_Mode=USART_Mode_Tx |USART_Mode_Rx; //若要用俩个功能,则或符号即可|
USART_InitStructure.USART_Parity= USART_Parity_No; //无校验位
USART_InitStructure.USART_StopBits= USART_StopBits_1; //一位停止位
USART_InitStructure.USART_WordLength= USART_WordLength_8b; //8位字长
USART_Init(USART2,&USART_InitStructure);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //可选择中断判断是否有数据录入
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel= USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1; //响应优先级
NVIC_Init(&NVIC_InitStructure);
//第三步,打开即可
USART_Cmd(USART2, ENABLE);
}
uint8_t LanYa_GetRxFlag(void) //判断是否有数据输入,有的话,使其复位为0
{
if(LanYa_RxFlag==1)
{
LanYa_RxFlag=0;
return 1;
}
return 0;
}
uint8_t LanYa_GetRxData(void) //获得输入的数据
{
return LanYa_RxData; //返回输入的数据
}
void USART2_IRQHandler(void) //中断函数主体(本蓝牙函数是利用中断)
{
if(USART_GetFlagStatus(USART2,USART_FLAG_RXNE)==SET) //如果是低电平
{
LanYa_RxData = USART_ReceiveData(USART2); //使得Data成为输入的数据
LanYa_RxFlag=1; //置一,我有数据来了
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除标志位
}
}
然后就是主模式的配置了,主从配置比较麻烦,我通过查阅b站配置主从,大概就是把主模式,传输地址,波特率,密码等内容,具体可以从b站找。
一下是主模式的蓝牙代码
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
uint8_t LanYa_RxData;
uint8_t LanYa_RxFlag;
void LanSend_Init(void) //初始化
{
//第一步,开启时钟(APB1——USART2)(GPIO引脚)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //注意,USART1是APB2的外设,USART2是APB1的外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //打开GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //TX是输出脚用复用推挽,RX是输入脚用输入模式(不分模式)一般浮空
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_2; //TX
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU; //Tx是输出脚用复用推挽,RX是输入脚用输入模式(不分模式)一般浮空或上拉
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_3; //Rx
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//第二步,初始化USART(结构体)
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate= 9600; //波特率
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ; //流控使用
USART_InitStructure.USART_Mode=USART_Mode_Tx |USART_Mode_Rx; //若要用俩个功能,则或符号即可|
USART_InitStructure.USART_Parity= USART_Parity_No; //无校验位
USART_InitStructure.USART_StopBits= USART_StopBits_1; //一位停止位
USART_InitStructure.USART_WordLength= USART_WordLength_8b; //8位字长
USART_Init(USART2,&USART_InitStructure);
//第三步,打开即可
USART_Cmd(USART2, ENABLE);
}
void LanYa_SendByte(uint8_t Byte)
{
USART_SendData(USART2, Byte);
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}
void LanYa_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++)
{
LanYa_SendByte(Array[i]);
}
}
void LanYa_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
LanYa_SendByte(String[i]);
}
}
uint32_t LanYa_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y --)
{
Result *= X;
}
return Result;
}
void LanYa_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++)
{
LanYa_SendByte(Number / LanYa_Pow(10, Length - i - 1) % 10 + '0');
}
}
int fputc(int ch, FILE *f)
{
LanYa_SendByte(ch);
return ch;
}
void LanYa_Printf(char *format, ...)
{
char String[100];
va_list arg;
va_start(arg, format);
vsprintf(String, format, arg);
va_end(arg);
LanYa_SendString(String);
}
获取摇杆的状态
原理:我不知道各位的摇杆是什么样的,我是通过AD来获取摇杆的x,y数据,不断读取获得状态,然后通过蓝牙输出即可。
原理简单,所以直接上AD的代码
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //打开ADC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //打开GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //打开ADCCLK的6分频
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //专属ADC的模式捏
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //专属ADC的模式捏
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//结构体初始化ADC
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode= ADC_Mode_Independent; //ADC1单打模式!
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right ; //右对齐就欧克啦
ADC_InitStructure.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None; //只用软件触发啦
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE ; //连续转换模式(ENABLE)
ADC_InitStructure.ADC_NbrOfChannel= 1; //通道数目
ADC_InitStructure.ADC_ScanConvMode= DISABLE; //扫描模式 看江科大啦
ADC_Init(ADC1,&ADC_InitStructure);
ADC_Cmd(ADC1,ENABLE); //ADC准备就绪捏
//但是我们要校准呢
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET); //没校准开始给我站在这里
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET); //等待校准完成捏
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}
uint16_t AD_GetValue_D(void)
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //连续触发可放在上面,这样也不需要等到转化完成啦↓
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET); //转换完成变成1就退出啦
return ADC_GetConversionValue(ADC1); //因为读取后自动清除标志位,所以不用清除标志位啦
}
//若是连续触发模式,可以将软件触发的函数挪到初始化的最后,只需要触发一次即可~
uint16_t AD_GetValue_E(void) //这个就是连续转换用的函数啦
{
return ADC_GetConversionValue(ADC1);
}
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //连续触发可放在上面,这样也不需要等到转化完成啦↓
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET); //转换完成变成1就退出啦
return ADC_GetConversionValue(ADC1); //因为读取后自动清除标志位,所以不用清除标志位啦
}
在设置状态时,可以用OLED显示AD参数,比较方便。
尾声:
作为我的第一个比较完整的学习作品,我还是比较满意的。
它让我巩固了PWM,串口,AD的知识,多次练习了接线,深入学习熟练掌握HC-05的使用方法,该作品适合初学者。文章来源:https://www.toymoban.com/news/detail-631770.html
通过以上的大概步骤,能够做出比较粗糙的摇杆小车,具体文件可以@我私发哦。文章来源地址https://www.toymoban.com/news/detail-631770.html
到了这里,关于大一萌新の作品:蓝牙主从模式摇杆控制小车和副产品手机遥控小车的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!