基于STM32F103C8T6的可调速泡泡机
【摘要】 STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器,常应用在嵌入式领域,其功能强大、性能优异、片上资源丰富、功耗低,是一款经典的嵌入式微控制器,这就是本课题所用到的芯片STM32F103C8T6。
【关键词】STM32F103C8T6;泡泡机;定时器PWM调速;stm32标准库;TB6612FNG电机驱动模块;0.96寸OLED屏幕;蓝牙JDY-31模块
1.选题确定
1.1问题背景
在公园游玩过程中,发现路边给小孩子玩的泡泡机,该机器可控制泡泡地输出。我的朋友看见旁边小孩在玩,想要扫码使用一台泡泡机来制造泡泡以美化拍照背景,但发现这个价格居然要15块钱3分钟,我们宁可多点一杯奶茶也不用这个泡泡机了,回来后我决定借单片机期末考试这个机会来自己制作一台可调速泡泡机。
1.2 目标效果
该泡泡机可以通过按键或蓝牙调节输出泡泡的速度,且可持续输出泡泡,并有OLED显示屏或手机蓝牙串口助手来查看相关数据。
1.3 需求分析
可调节输出泡泡的速度,该过程需要使用PWM调节电机的速度;LED呼吸灯,使用PWM调光;OLED显示屏输出相关数据;可通过手机蓝牙操控。
. 设计过程
本次课题主要是进行对STM32F103C8T6和TB6612FNG电机驱动模块功能探索使用。内容主要是通过按钮来控制STM32F103C8T6输出的PWM信号,再通过TB6612FNG电机驱动模块来进行双电机的驱动。
2.1 方案选定
2.1.1方案一:
采用一个SG90舵机加一个微型130马达,SG90舵机作为泡泡膜采集生成器,微型130马达作为吹泡泡装置。此方案输出泡泡过程较慢,且使用面包板进行测试PWM驱动舵机时泡泡膜易破,且舵机每次只能旋转180°,并不能持续输出泡泡。
2.1.2方案二:
采用两个微型130马达,该方案中一个微型130马达用于泡泡膜的采集,360°旋转泡泡采集器,另一个微型130马达作为吹泡泡装置。 (经测试,此方案中,泡泡机马达在水中阻力过大,无法正常运行)
2.1.3方案三:
采用一个减速电机和一个微型130马达,采用一个微型130马达作为吹泡泡装置,再采用减速电机采集泡泡膜,此方案能持续可调速地输出泡泡,且泡泡生成更加稳定。
2.2 技术路线
STM32F103C8T6通过PWM输出控制TB6612FNG电机驱动模块,再通过该驱动模块控制相应电机,同时0.96寸OLED显示屏上显示相关数据或通过蓝牙模块将数据传输到手机上并用手机操作数据。
2.3 系统结构
-
初始化串口、TIM定时器、中断、USART、NVIC、PWM
-
OLED显示屏输出相关选项
-
等待按键输入或手机蓝牙输入
-
单片机处理
-
输出PWM给TB6612FNG电机驱动模块分别驱动电机采集泡泡液和吹泡泡
2.4 模块原理
2.4.1 Key模块原理
初始化GPIO,通过GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_x)获取按键状态。
初始化GPIO,启动并配置TIM2、TIM3, 通过TIM_SetCompare设置PWM速度。
2.4.3 电机驱动模块实现
初始化GPIO、PWM,配置正反转以及PWM调速。
2.4.4 蓝牙模块实现
初始化GPIO,配置USART1、NVIC,分别写“读”“写”函数,最后写一个蓝牙模块运作函数。
2.4.5 OLED显示模块实现
初始化GPIO,用PCtoLCD2002.exe 软件确定OLED相关字符,分别写不同数据类型的输出函数,最后写一个总控OLED屏幕的函数。
2.5 实现过程
2.5.1Key模块实现
//按键初始化
void Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
//按键值获取
uint8_t Key_GetNum(void)
{
uint8_t KeyNum=0;
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==0);
Delay_ms(20);
KeyNum=1;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==0);
Delay_ms(20);
KeyNum=2;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0);
Delay_ms(20);
KeyNum=3;
}
return KeyNum;
}
2.5.2 PWM模块实现
//PWM初始化
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=100-1;//PSC周期 要1s 10000是1s 1000是0.1s
TIM_TimeBaseInitStructure.TIM_Prescaler=36-1;//ARR自动重装值,
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器,高级计时器才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCNPolarity=TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse=50;//CCR
TIM_OC3Init(TIM2,&TIM_OCInitStructure);
TIM_OC3Init(TIM3,&TIM_OCInitStructure);
TIM_Cmd(TIM2,ENABLE);
TIM_Cmd(TIM3,ENABLE);
}
//设置风扇电机PWM
void PWM_SetCompare3_TIM2(uint16_t Compare)
{
TIM_SetCompare3(TIM2,Compare);
}
//设置减速电机PWM
void PWM_SetCompare3_TIM3(uint16_t Compare)
{
TIM_SetCompare3(TIM3,Compare);
}
2.5.3 电机驱动模块实现
//电机初始化
void Motor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
PWM_Init();
}
//风扇电机速度输入
void Motor_SetSpeed_2(uint8_t Speed)
{
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_3);
PWM_SetCompare3_TIM2(Speed);
}
//减速电机速度输入
void Motor_SetSpeed_3(uint8_t Speed)
{
GPIO_SetBits(GPIOA,GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
PWM_SetCompare3_TIM3(Speed);
}
2.5.4 蓝牙模块实现
//蓝牙模块初始化
void Serial_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//TX引脚是USART外设的输出引脚,用复用推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//RX引脚是USART外设的输入引脚,用上拉输入或者浮空输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_InitStructure.USART_BaudRate=9600;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;//Tx电脑接受,Rx电脑发送
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启RXNE标志位到NVIC的输出
//配置NVIC (中断)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
//USART_IT_RXNE一但置1就会像NVIC申请发起中断
USART_Cmd(USART1,ENABLE);
}
//蓝牙模块发送一个字节
void Serial_SendByte(uint8_t Byte)//发送一个字节
{
USART_SendData(USART1,Byte);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
}
//蓝牙模块将数据打印到手机上
void Serial_Printf(char *format,...)//“...”带表可变的参数列表
{
char String[100];
va_list arg;//容器
va_start(arg,format);
vsprintf(String,format,arg);
va_end(arg);
Serial_SendString(String);
}
//蓝牙模块通过USART读取数据
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
{
Serial_RxData=USART_ReceiveData(USART1);
Serial_RxFlag=1;
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
//蓝牙模块运作模块
void Bluetooth_Operation(void)
{
//uint16_t temp;
FLAG1:
Serial_Printf("请输入您的选择:\r\n");
Serial_Printf("1.风扇电机调速\r\n");
Serial_Printf("2.减速电机调速\r\n");
Serial_Printf("风扇电机速度:%3d \r\n减速电机速度:%3d\r\n",UpSpeed,DwSpeed);
Serial_Printf("输入3退出蓝牙模式\r\n");
while(Serial_GetRxFlag()==RESET);
if(Serial_RxData==1)
{
FLAG2:
Serial_RxData=0;
Serial_Printf("请输入所需风扇电机的速度:\r\n");
Serial_Printf("(输入为十六进制0-64,否则返回)\r\n");
Serial_Printf("0-0 19-25 32-50 48-75 64-100\r\n");
Serial_Printf("当前风扇电机速度:%3d",UpSpeed);
while(Serial_GetRxFlag()==RESET);
//temp=Serial_RxData;
if(Serial_RxData<=100)
{
UpSpeed=Serial_RxData;
Motor_SetSpeed_2(UpSpeed);
Serial_RxData=0;
//Serial_Printf("当前风扇电机速度:%3d",UpSpeed);
OLED_ShowString(4,1,"Usp:");OLED_ShowNum(4,5,UpSpeed,3);
goto FLAG2;
}
else
{
Serial_RxData=0;
goto FLAG1;
}
}
else if(Serial_RxData==2)
{
FLAG3:
Serial_RxData=0;
Serial_Printf("请输入所需减速电机的速度:\r\n");
Serial_Printf("(输入为十六进制0-64,否则返回)\r\n");
Serial_Printf("0-0 19-25 32-50 48-75 64-100\r\n");
Serial_Printf("当前减速电机速度:%3d",DwSpeed);
while(Serial_GetRxFlag()==RESET);
if(Serial_GetRxFlag()==RESET)
{
//temp=Serial_RxData;
if(Serial_RxData<=100)
{
DwSpeed=Serial_RxData;
Motor_SetSpeed_3(DwSpeed);
Serial_RxData=0;
//Serial_Printf("当前减速电机速度:%3d",DwSpeed);
OLED_ShowString(4,8," Dsp:");OLED_ShowNum(4,13,DwSpeed,3);
goto FLAG3;
}
else
{
Serial_RxData=0;
goto FLAG1;
}
}
}
else if(Serial_RxData==3)
{
OLEDorBluetooth_Flag=0;
OLED_ShowString(1,1,"Mode:Key ");
}
}
2.5.5 OLED显示模块实现
//OLED初始化
void OLED_I2C_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//OLED显示屏运行
void OLED_Showing(void)
{
OLED_ShowString(1,1,"Mode:Key ");
OLED_ShowString(2,1,"1.Up Motor Speed");
OLED_ShowString(3,1,"2.Do Motor Speed");
OLED_ShowString(4,1,"Usp:");OLED_ShowNum(4,5,UpSpeed,3);
OLED_ShowString(4,8," Dsp:");OLED_ShowNum(4,13,DwSpeed,3);
Key1=Key_GetNum();
switch(Key1)
{
case 1:
OLED_Clear();
Key1=0;
OLED_ShowString(1,1,"Please Select:");
OLED_ShowString(2,1,"1.Increase");
OLED_ShowString(3,1,"2.Slow 3.Back");
Flag1=1;
while(Flag1)
{
Key1=Key_GetNum();
if(Key1==1&&UpSpeed<100)
UpSpeed+=5;
if(Key1==2&&UpSpeed>0)
UpSpeed-=5;
Motor_SetSpeed_2(UpSpeed);
if(Key1==3)
Flag1=0;
OLED_ShowString(4,1,"UMSpeed:");OLED_ShowNum(4,9,UpSpeed,3);
}
break;
case 2:
OLED_Clear();
Key1=0;
OLED_ShowString(1,1,"Please Select:");
OLED_ShowString(2,1,"1.Increase");
OLED_ShowString(3,1,"2.Slow 3.Back");
Flag1=1;
while(Flag1)
{
Key1=Key_GetNum();
if(Key1==1&&DwSpeed<100)
DwSpeed+=5;
if(Key1==2&&DwSpeed>0)
DwSpeed-=5;
Motor_SetSpeed_3(DwSpeed);
if(Key1==3)
Flag1=0;
OLED_ShowString(4,1,"DwSpeed:");OLED_ShowNum(4,9,DwSpeed,3);
}
break;
case 3:
OLEDorBluetooth_Flag=1;
OLED_ShowString(1,1,"Mode:Bluetooth");
break;
default:
break;
}
}
3.设计结果
本次课题主要利用的是STM32F103C8T6作为基本开发版,再利用TB6612FNG电机驱动模块来进行数据输出,同时通过按键和蓝牙进行数据地输入,结果非常好,基本目的已达成,可以通过按键或蓝牙调节输出泡泡的速度,且可持续输出泡泡,并有OLED显示屏或手机蓝牙串口助手来查看相关数据。
3.3 后续改进
该泡泡机由于在水中当液面下降时阻力会下降,使用PWM调速在液面下降后会使阻力减小,速度变快,可以使用PID算法来维持减速电机按设定速度运转。
此外,泡泡机运行过程中可添加水位监测器和涡轮机添加泡泡液以更持久地输出泡泡。
我参考市面上的泡泡机发现他们是用一个涡轮机将水抽上来并挤压软管使其在管口同时完成抽泡泡液和吹泡泡,后续可以改进。
4.结束语
4.结束语
本项目以 STM32F103C8T6 为核心构建可调速泡泡机,从萌生出自主创作的想法,到一步步确定目标效果与需求分析,历经多种方案的筛选与尝试,最终成功打造出具备按键与蓝牙双模式调速、可持续出泡且能通过 OLED 屏或手机蓝牙串口助手展示数据的泡泡机,这一过程充满挑战与收获。
在设计过程中,我深入探索了 STM32F103C8T6 与 TB6612FNG 电机驱动模块的功能特性,通过按钮精准控制 PWM 信号以驱动双电机运转。
回顾整个项目,不仅在硬件电路设计与软件编程上实现了预期功能,更在实践中积累了宝贵的经验,对嵌入式系统开发有了更为深刻的理解与感悟。然而,我们也清楚地认识到当前作品仍存在一些可优化之处,例如在不同液面高度下泡泡机速度稳定性的问题,以及整体结构与商用泡泡机相比在紧凑性和功能性集成度上尚有提升空间。但这也为后续的进一步改进提供了明确的方向,我们将秉持创新与探索精神,不断完善这一作品,力求在嵌入式应用领域创造出更多具有实用价值与创意的成果,为科技与生活的融合贡献一份力量。
完整版在我主页内容pdf免费下载