【前言】
此篇的目的是制作一个用蓝牙远程控制的小车。
【硬件】
1、网购的一个小车底盘

2、STM32F103RC开发板
3、蓝牙模块:

原来的底盘是用51来做主控的,我这里修改为stm32f103来做主控。
【stm32控制设计】
1、小车底盘上面有两个直流有刷减速电机,由一块L293D进行驱动。其原理图如下:

因此我使用STM32F103的四个IO来控制方向即IN1-IN4,使用两路PWM来控制EN1与EN2,分别控制两个电机的速度。
使用stm32cubeMX配置PA4-PA7为普通GPIO输出。

配置两路PWM如下:

配置USART1为蓝牙串口接入,串口波特率为115200,使能串口空闲中断。

在串口初始化后,加入中断及空闲中断的开启代码:
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
修改串口1的中断函数如下:
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == SET)
{
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
{
USART1_Head.RxBuf[USART1_Head.Rx_count++] = USART1->DR;
}
}
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) == SET)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
USART1_Head.Rx_over = 1;
}
/* USER CODE END USART1_IRQn 1 */
}添加串口解析函数如下:
void Uart_Analysis(uint8_t Rx_over)
{
uint8_t ctrl ;
uint8_t left_dir ; // 左轮方向(bit7-bit6)
uint8_t right_dir ; // 右轮方向(bit1-bit0)
uint8_t left_speed ; // 左轮速度(TIM2_CH4=PB11)
uint8_t right_speed ; // 右轮速度(TIM2_CH3=PB10)
if(Rx_over == 1)
{
printf("rcv:%s",USART1_Head.RxBuf);
if(strstr((char*)USART1_Head.RxBuf, "+CONNECTED") != NULL)
{
printf("max2 isreadey\r\n");
return;
}
else if(USART1_Head.RxBuf[0] != FRAME_HEADER || USART1_Head.RxBuf[5] != FRAME_TAIL)
{
Rx_over = 0;
UART_ClearArray(&USART1_Head);
return;
}
// 校验位验证(字节1 ^ 字节2 ^ 字节3 == 字节4)
uint8_t check = USART1_Head.RxBuf[1] ^ USART1_Head.RxBuf[2] ^ USART1_Head.RxBuf[3];
if (check != USART1_Head.RxBuf[4]) {
Rx_over = 0;
UART_ClearArray(&USART1_Head);
return;
}
// 解析控制位(字节1)
ctrl = USART1_Head.RxBuf[1];
left_dir = (ctrl >> 6) & 0x03; // 左轮方向(bit7-bit6)
right_dir = ctrl & 0x03; // 右轮方向(bit1-bit0)
// 设置PWM速度(0x00~0xFF映射到0~99,因ARR=99)
left_speed = USART1_Head.RxBuf[2]; // 左轮速度(TIM2_CH4=PB11)
right_speed = USART1_Head.RxBuf[3]; // 右轮速度(TIM2_CH3=PB10)
blue_car_tontrl(ctrl,left_dir,right_dir,left_speed ,right_speed) ;
UART_ClearArray(&USART1_Head);
}
}具体的解析协议如下:

解析函数放入main的大循环中周期调用。
当解析数据就位后,执行控制函数:
void blue_car_tontrl(uint8_t ctrl,
uint8_t left_dir,
uint8_t right_dir,
uint8_t left_speed ,
uint8_t right_speed)
{
// 控制左轮方向(IN1=PA4,IN2=PA5)
switch (left_dir) {
case 0x00: // 停止(IN1=0,IN2=0)
GPIOA->ODR &= ~(GPIO_ODR_ODR4 | GPIO_ODR_ODR5);
break;
case 0x01: // 前进(IN1=1,IN2=0)
GPIOA->ODR |= GPIO_ODR_ODR4; // PA4=1
GPIOA->ODR &= ~GPIO_ODR_ODR5; // PA5=0
break;
case 0x02: // 后退(IN1=0,IN2=1)
GPIOA->ODR &= ~GPIO_ODR_ODR4; // PA4=0
GPIOA->ODR |= GPIO_ODR_ODR5; // PA5=1
break;
case 0x03: // 刹车(IN1=1,IN2=1)
GPIOA->ODR |= GPIO_ODR_ODR4 | GPIO_ODR_ODR5;
break;
}
// 控制右轮方向(IN3=PA6,IN4=PA7)
switch (right_dir) {
case 0x00: // 停止(IN3=0,IN4=0)
GPIOA->ODR &= ~(GPIO_ODR_ODR6 | GPIO_ODR_ODR7);
break;
case 0x01: // 前进(IN3=1,IN4=0)
GPIOA->ODR |= GPIO_ODR_ODR6; // PA6=1
GPIOA->ODR &= ~GPIO_ODR_ODR7; // PA7=0
break;
case 0x02: // 后退(IN3=0,IN4=1)
GPIOA->ODR &= ~GPIO_ODR_ODR6; // PA6=0
GPIOA->ODR |= GPIO_ODR_ODR7; // PA7=1
break;
case 0x03: // 刹车(IN3=1,IN4=1)
GPIOA->ODR |= GPIO_ODR_ODR6 | GPIO_ODR_ODR7;
break;
}
// 设置PWM速度(0x00~0xFF映射到0~99,因ARR=99)
TIM2->CCR4 = (left_speed * 100) / 255; // 占空比 = (CCR / ARR) * 100%
TIM2->CCR3 = (right_speed * 100) / 255;
}最后在main中添加开启pwm两路输出的命令:
/* USER CODE BEGIN 2 */
TIM2->CR1 |= TIM_CR1_CEN; // 启动定时器(CubeMX可能已启动,此处冗余)
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
Uart_Analysis(USART1_Head.Rx_over);
HAL_Delay(1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}到此代码编写结束
成品的如下图所示:

通过蓝牙主发送:FF 41 80 80 41 A5就可以让蓝牙小车向前。通过发送: FF 00 00 00 00 A5让蓝牙小车停止。
附工程源码
我要赚赏金
