前言
这周刚好做了一个小项目,需要用到单片机控制一个小车移动,在实验室搜刮了一些材料,进行了一些调试工作,感觉也是蛮有意思的。小车的底盘用的是之前电赛剩下的,单片机用的是最小系统板,蓝牙模块是hc05,直流电机也是最普通的小马达。
软硬件
调试软件:keil5
主控板:stm32f103c8t6
蓝牙模块:HC-05
电机驱动模块:TB6612
app:蓝牙调试宝
驱动电机
下图是TB6612驱动模块原理图
AIN1/AIN2,BIN1/BIN2,PWMA/PWMB为控制信号输入端,AO1/AO2,BO1/BO2为两路电机控制输出端。STBY正常工作接5v电压,VCC接3.3v,VM接5v,GND接地。
引脚接线情况:
TB6612 | 单片机 |
VM | 5V |
VCC | 3.3V |
GND | GND |
PWMA | A2 |
PWMB | A3 |
AIN1 | A4 |
AIN2 | A5 |
BIN1 | A6 |
BIN2 | A7 |
AO1 | 电机1(M+) |
AO2 | 电机1(M-) |
BO1 | 电机2(M+) |
BO2 | 电机2(M-) |
定时器的PWM模式驱动电机
Motor.h
#ifndef __MOTOR_H
#define __MOTOR_H
void Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);
void Motor_SetSpeed2(int8_t Speed);
void Bluetooth_Receive_Handler(uint8_t data);
#endif
Motor.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
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 = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM_Init();
}
void Motor_SetSpeed(int8_t Speed)
{
if (Speed >= 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare3(Speed);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare3(-Speed);
}
}
void Motor_SetSpeed2(int8_t Speed)
{
if (Speed >= 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_6);
GPIO_ResetBits(GPIOA, GPIO_Pin_7);
PWM_SetCompare4(Speed);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_6);
GPIO_SetBits(GPIOA, GPIO_Pin_7);
PWM_SetCompare4(-Speed);
}
}
pwm.h
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);
void PWM_SetCompare4(uint16_t Compare);
#endif
pwm.c
#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_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
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_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC4Init(TIM2, &TIM_OCInitStructure); // 初始化TIM2的通道4
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM2, Compare);
}
void PWM_SetCompare4(uint16_t Compare)
{
TIM_SetCompare4(TIM2, Compare); // 设置TIM2的通道4的捕获比较寄存器值
}
蓝牙模块HC05
引脚接线情况:
HC05 | 单片机 |
RXD | PA9 |
TXD | PA10 |
GND | GND |
VCC | VCC(5v) |
蓝牙控制我选择的是手机app控制,应用商店直接下载蓝牙调试宝。
连接成功后,在手机app上输入指令1,电机转动,输入0,电机停止。
附上代码:
main.c
#include "main.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Motor.h"
#include "Key.h"
#include "USART1.h"
//时间结构体 在RTC_day.h中初始化了
//时间结构体定义在 RTC_day.h
//刷新时间标志
uint16_t TimeDisplay_cnt,TimeDisplay,T;
uint8_t KeyNum;
int8_t Speed;
int receivedChar =0; // 默认接收到的0
int main(void)
{
init_ALL(); //初始化所有函数
Motor_Init(); // 初始化电机
Motor_SetSpeed(0); // 设置电机1速度
Motor_SetSpeed2(0); //设置电机2速度
while (1)
{
// 检查串口是否有数据可接收
receivedChar =(int)USART_ReceiveData(USART1); // 读取接收到的字符
Delay_ms(200);
// 根据接收到的字符控制电机
if (receivedChar == 1)
{
Delay_ms(200);
Motor_SetSpeed(100); // 如果接收到数字1,电机1转动
Motor_SetSpeed2(100); // 如果接收到数字1,电机2转动
Delay_ms(200);
}
else if (receivedChar == 0)
{
Delay_ms(200);
Motor_SetSpeed(0); // 如果接收到数字0,电机1停止
Motor_SetSpeed2(0); // 如果接收到数字0,电机2停止
Delay_ms(200);
}
}
}
//初始化所有函数:
void init_ALL(void)
{
SysTick_Init(72); //初始化滴答计时器
Timer2_Init(); //初始化定时器2
Usart1_Init(115200); //串口1初始化
}
//定时器2中断服务函数
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
if(++TimeDisplay_cnt==100) //定时器刷新时间
{
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断
}
}
USART1.c:
#include "USART1.h"
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{USART_ClearFlag(USART1, USART_FLAG_RXNE);}
//返回收到的任意数据
USART_SendData(USART1,USART_ReceiveData(USART1));
}
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(USART1, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return (ch);
}
///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
/* 等待串口输入数据 */
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
//选择串口发送数据--自定义Printf
void UsartPrintf (USART_TypeDef *USARTx, char *fmt,...)
{
unsigned char UsartPrintfBuf[296]; //最大长度296
va_list ap;
unsigned char *pStr = UsartPrintfBuf;
va_start(ap, fmt);
vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化
va_end(ap);
while(*pStr != 0)
{
USART_SendData(USARTx, *pStr++);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
}
//发送一个字节
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
//发送一个字节数据到USART
USART_SendData(pUSARTx,ch);
// 等待发送数据寄存器为空
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
//发送8位的数组
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
uint8_t i;
for(i=0; i<num; i++)
{
//发送一个字节数据到USART
Usart_SendByte(pUSARTx,array[i]);
}
//等待发送完成
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
//发送字符串
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');
//等待发送完成
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}
//发送一个16位数
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
uint8_t temp_h, temp_l;
// 取出高八位
temp_h = (ch&0XFF00)>>8;
//取出低八位
temp_l = ch&0XFF;
//发送高八位
USART_SendData(pUSARTx,temp_h);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
//发送低八位
USART_SendData(pUSARTx,temp_l);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
不过上面的代码是很基础的,想让小车真正流畅地跑起来还需要做很多改进,电源模块用的是DC-DC模块。
下面测试一下电源模块:
下面是用蓝牙调试宝来控制电机:
不过,目前都是用杜邦线连接,看着不太美观,在嘉立创上画了免费的板子做一下集成,还没有到,到时候用上实验室的飞无人机的4s电池,应该能跑起来吧。
标签:Pin,USART,app,TB6612FNG,TIM,hc05,Motor,GPIO,void From: https://blog.csdn.net/qq_45509667/article/details/140324458