Freshman's new work: Bluetooth master-slave mode joystick control car and by-product mobile phone remote control car

(Purely new, I have been learning MCU for half a year. This is a work I came home from during the winter vacation. I learned from Jiang Ke University and wrote a blog to record my experience after I realized it.) (roughly)

Bluetooth master-slave mode Joystick control car, I divided it into three parts: 1. Let the car move 2. Bluetooth master-slave connection and signal sending and receiving 3. Obtain the status of the joystick and control the steering of the car

The first thing is to get the car moving

Target: internal power supply, adjustment parameter steering.

Principle: To make the car move, it only needs to be powered on. By controlling the CCR of the PWM to adjust the duty cycle, the current with different frequencies can be output to the motor, and the rotation speed of the car can be adjusted. When the speed of the two wheels is different, the car will turn. The internal power supply is the battery and L298n. The battery outputs 7-12v voltage, which can be connected to the L298n motor drive module to output 5v voltage, and then converted to 3.3v to supply power to the microcontroller. I used the car chassis and circuit board from Taobao merchants. The only function of this circuit is to convert 5v to 3.3v for power supply. In addition, the power module can also be used to convert 5v to 3.3v on the breadboard.

Process: For me, the difficulty lies in the power supply. The power supply is changed from two 3.7v to 3.3v through two conversions.

Specific PWM code and motor code (most of them are from Jiangke University, there is nothing to say)

#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);
}

Let two Bluetooth master-slave communicate

Goal: Before realizing the master-slave mode, my first goal is to enable the mobile phone to connect to Bluetooth and send information to the car

Process: I use HC-05 for Bluetooth, which can be configured as a master-slave, and the HC-05 is in slave mode when shipped from the factory. First, change the baud rate of HC-05 to 9600 through AT mode. Then connect HC-05 to the breadboard, use usart2 as the communication medium, connect Rx and Tx according to the pin definition table, write the code for receiving data, and connect to Bluetooth through the mobile app to send.

The following is the bluetooth slave mode code (interrupts are used)

#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);        //清除标志位
    }
}

Then there is the configuration of the master mode. The master-slave configuration is more troublesome. I configured the master-slave by consulting station b, which is probably the master mode, transmission address, baud rate, password, etc., which can be found from station b.

The following is the bluetooth code of the main mode

#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);
}

Get the status of the joystick

Principle: I don’t know what your joystick is like. I use AD to obtain the x, y data of the joystick, read the status continuously, and then output it through Bluetooth.

The principle is simple, so go directly to the AD code

#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);                //因为读取后自动清除标志位,所以不用清除标志位啦
}

When setting the state, you can use OLED to display AD parameters, which is more convenient.

end:

As my first relatively complete learning work, I am quite satisfied.

它让我巩固了PWM,串口,AD的知识,多次练习了接线,深入学习熟练掌握HC-05的使用方法,该作品适合初学者。

通过以上的大概步骤,能够做出比较粗糙的摇杆小车,具体文件可以@我私发哦。

Guess you like

Origin blog.csdn.net/ChiShangying/article/details/128780550