Abstract: 用STM32做的蓝牙小车

用到的器材

  1. STM32C8T6最小系统板

  2. JDY-31蓝牙模块

  3. 3.3V稳压模块

  4. L298N模块

  5. 蜂鸣器模块

  6. 2轮智能小车底盘套件

  7. 3.7V 18650锂电池及电池盒

  8. 1.5V碱性电池及电池盒

代码部分

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Motor.h"
#include "JDY31.h"


//U-加速 D-减速 L-左转 R-右转 S-前进 B-后退 1-蜂鸣器响

uint8_t Speed;

void Di_Init(void);
void Di(void);

int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

Di_Init();
JDY31_Init();
Motor_Init();

while(1)
{
// MotorA_SetSpeed(Speed);
// MotorB_SetSpeed(Speed);

if (JDY31_GetRxFlag()==1)
{
//处理接收到的数据
if (JDY31_RxString[0]=='U') //加速
{
if (Speed<=70)
{
Speed+=30;
}
else
{
Speed=100;
}
}
else if (JDY31_RxString[0]=='D') //减速
{
if (Speed>=30)
{
Speed-=30;
}
else
{
Speed=0;
}
}
else if (JDY31_RxString[0]=='L') //左转
{
MotorA_SetSpeed(Speed);
MotorB_SetSpeed(Speed/2);

Delay_ms(200);

MotorA_SetSpeed(0);
MotorB_SetSpeed(0);

}
else if (JDY31_RxString[0]=='R') //右转
{
MotorB_SetSpeed(Speed);
MotorA_SetSpeed(Speed/2);

Delay_ms(200);

MotorB_SetSpeed(0);
MotorA_SetSpeed(0);
}
else if (JDY31_RxString[0]=='S') //前进
{
MotorA_SetSpeed(Speed);
MotorB_SetSpeed(Speed);

Delay_ms(200);

MotorA_SetSpeed(0);
MotorB_SetSpeed(0);
}
else if (JDY31_RxString[0]=='B') //后退
{
MotorA_SetSpeed(-Speed);
MotorB_SetSpeed(-Speed);

Delay_ms(200);

MotorA_SetSpeed(0);
MotorB_SetSpeed(0);
}
else if (JDY31_RxString[0]=='1') //蜂鸣器响
{
Di();
}
//JDY31_SendData(JDY31_RxString);
}
}
}

void Di_Init(void)
{
//蜂鸣器初始化,PB1为I/O
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启时钟

GPIO_InitTypeDef GPIO_InitStructure; //GPIO配置
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_1; //发送线
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;

GPIO_Init(GPIOB,&GPIO_InitStructure);
}

void Di(void)
{
//蜂鸣器发声
GPIO_SetBits(GPIOB,GPIO_Pin_1);
Delay_ms(100);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
}

PWM.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_6; //TIM2_CH2 TIM3_CH1
GPIO_Init(GPIOA, &GPIO_InitStructure);

TIM_InternalClockConfig(TIM2); //用内部时钟驱动TIM2的时基单元
TIM_InternalClockConfig(TIM3); //用内部时钟驱动TIM3的时基单元

TIM_TimeBaseInitTypeDef Time_ClockInitStructure;
Time_ClockInitStructure.TIM_ClockDivision= TIM_CKD_DIV1; //指定时钟分频
Time_ClockInitStructure.TIM_CounterMode= TIM_CounterMode_Up; //计数模式
Time_ClockInitStructure.TIM_Period= 100-1; //ARR自动重装器的值 定时频率: CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)
Time_ClockInitStructure.TIM_Prescaler= 720-1; //PSC预分频器的值
Time_ClockInitStructure.TIM_RepetitionCounter= 0; //重复计数器的值

TIM_TimeBaseInit(TIM2,&Time_ClockInitStructure);
TIM_TimeBaseInit(TIM3,&Time_ClockInitStructure);

TIM_OCInitTypeDef TIME_OCInitStructure;
TIM_OCStructInit(&TIME_OCInitStructure); //结构体赋初始值
TIME_OCInitStructure.TIM_OCMode= TIM_OCMode_PWM1; //设置输出比较模式
TIME_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_High; //设置极性
TIME_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable; //输出使能
TIME_OCInitStructure.TIM_Pulse= 0; //设置CCR PWM频率=定时频率 PWM占空比:Duty=CCR/(ARR+1) PWM分辨率:1/(ARR+1)

TIM_OC2Init(TIM2,&TIME_OCInitStructure);
TIM_OC1Init(TIM3,&TIME_OCInitStructure);

TIM_Cmd(TIM2,ENABLE); //开启定时器
TIM_Cmd(TIM3,ENABLE); //开启定时器
}

void PWM_SetCompare1_TIM3(uint16_t Compare) //TIM3_CH1
{
TIM_SetCompare1(TIM3,Compare); //设置CCR的值
}

void PWM_SetCompare2_TIM2(uint16_t Compare) //TIM2_CH2
{
TIM_SetCompare2(TIM2,Compare); //设置CCR的值
}

void PWM_SetPrescaler(uint16_t prescaler)
{
TIM_PrescalerConfig(TIM2,prescaler,TIM_PSCReloadMode_Immediate);
}

Motor.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "stm32f10x.h"                  // Device header
#include "PWM.h"

#define INA1 GPIO_Pin_4
#define INA2 GPIO_Pin_5
#define INB1 GPIO_Pin_2
#define INB2 GPIO_Pin_3

//TIM2控制电机A,TIM3控制电机B

void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = INA1 | INA2 | INB1 | INB2; //电机方向控制脚 4,5 2,3
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

PWM_Init();
}

void MotorA_SetSpeed(int8_t Speend)
{
if (Speend>=0)
{
GPIO_SetBits(GPIOA,INA2);
GPIO_ResetBits(GPIOA,INA1);
PWM_SetCompare2_TIM2(Speend);
}
else
{
GPIO_ResetBits(GPIOA,INA2);
GPIO_SetBits(GPIOA,INA1);
PWM_SetCompare2_TIM2(-Speend);
}
}

void MotorB_SetSpeed(int8_t Speend)
{
if (Speend>=0)
{
GPIO_SetBits(GPIOA,INB1);
GPIO_ResetBits(GPIOA,INB2);
PWM_SetCompare1_TIM3(Speend);
}
else
{
GPIO_ResetBits(GPIOA,INB1);
GPIO_SetBits(GPIOA,INB2);
PWM_SetCompare1_TIM3(-Speend);
}
}

JDY31.h

在之前的文章

成果分析

  • 制作过程中想使用OLED显示屏,但是一直显示不正常,排除显示屏和代码的问题,原因未知,推测为接的线相互影响导致I2C通信不正常
  • 制作时使用了两个电源分别给电机驱动模块和单片机供电,在底盘面积足够的情况下应该是不错的选择
  • JDY-31模块莫名出问题,但已解决
  • L298N模块使用时需要和单片机共地(重要
  • 成品小车出现轨迹倾斜的情况,可能的原因:车轮或电机固定时有倾斜、电机供电线不同影响电机转速
  • 二轮小车底盘存在一些不重要的问题,之后考虑使用四轮的底盘