赞
踩
一、项目时间:2023.7.24~11.26
二、实现效果:通过蓝牙控制小车运动与模式转换
模式一:循迹模式
模式二:跟踪模式
模式三:音乐模式
模式四:控制运动模式
三、使用模块:
下面是超级蓝牙小车实物图:
需要用到的资源如下:
1,车轮:TIM2-CH1,CH2 => PA0,PA1 + PB0,PB1,PB2,PB10,PB11
2,蓝牙:USART-TX,RX => PA9 PA10
3,超声波:TIM-CH1,CH2 => PA6 PA7
4,OLED: PB8,PB9
5,ADC+DMA: PA3,PA4(采集两个数据:温度与光强)
6,舵机:TIM4-CH2 => PB7
7,循迹模块: PA15,PB3~6
8,炫灯
9:蜂鸣器
下面的表格是引脚使用的直观表格
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
PA | 1 | 1 | X | 5 | 5 | 9 | 3 | 3 | 8 | 2 | 2 | 8 | 8 | / | / | 7 |
PB | 1 | 1 | / | 7 | 7 | 7 | 7 | 6 | 4 | 4 | 1 | 1 | 8 | 8 | 8 | 8 |
四、代码
因为使用到的模块很多,一些基础配置的代码就不放出来了,下面是一些重要部分的代码
1,循迹模块代码 Trailing.c
- #include "stm32f10x.h" // Device header
- #include "delay.h"
- #include "Trailing.h"
- #include "Motor.h"
- #include "PID.h"
-
- int Speed_Begin = 50; //extern 后面需要加上->变量以及变量的类型
-
-
- /*====================================
- 函数 :五路循迹模块加权函数
- 参数 :无
- 返回值 :返回五路加权的结果
- 描述 :通过对五路循迹情况进行加权,输出
- 到PID函数作为误差参数,即将数字情
- 况转变为误差。
- ====================================*/
- int Trailing_Speed(void)
- {
- // int Flag;
- int Sum,Speed_1;
- Sum = 0;
- // Flag = 0;
- if(LED1==1) Sum=Sum-20;
- if(LED2==1) Sum=Sum-10;
- if(LED3==1) Sum=Sum ;
- if(LED4==1) Sum=Sum+10;
- if(LED5==1) Sum=Sum+20;
- Speed_1 = (int)Sum;
- return(Speed_1);
- // if(LED1==0) Sum=Sum-20,Flag++;
- // if(LED2==0) Sum=Sum-10,Flag++;
- // if(LED3==0) Sum=Sum ,Flag++;
- // if(LED4==0) Sum=Sum+10,Flag++;
- // if(LED5==0) Sum=Sum+20,Flag++;
- // Speed_1 = (int)Sum / Flag + Speed_Begin;
- // return(Speed_1);
- }
-
- /*====================================
- 函数 :运动轨迹调整函数
- 参数 :无
- 返回值 :无
- 描述 :先通过pid计算出现在的速度值
- 如,速度值等于初始值,直走
- 速度值小于初始值,左转
- 速度值大于初始值,右转
- ====================================*/
- void Trailing_Adjust(void)
- {
- int Speed;
- Speed = Speed_Begin + Position_PID(Trailing_Speed());
-
- if(Speed == Speed_Begin) //做一个速度的大小判断,进而判断此时对应的转向
- {
- delay_ms(1);
- Motor_Straight(Speed);
- }
- else if (Speed < Speed_Begin) Motor_Left(Speed - 2 *Position_PID(Trailing_Speed()));
- else Motor_Right(Speed);
- }
-
- /*====================================
- 函数 :五路循迹模块情况汇总函数
- 参数 :无
- 返回值 :五路循迹情况
- 描述 :通过对五路循迹的情况进行计算得到
- 一个五位数的数字,便于输出在OLED
- 显示屏上,例:00001—最右边一个
- 被遮挡
- ====================================*/
- int Track_State(void)
- {
- u16 LED;
- LED = (LED1 * 10000) + (LED2 * 1000)+ (LED3 * 100) + (LED4 * 10) + LED5;
- return (LED);
- }
-
-
- //u8 Trailing_Filter_One(u8 Value,u8 Get_Io) //滤波1
- //{
- // u8 Count=0;
- // u8 New_Value;
- // New_Value=Get_Io;
- // while(Value!=New_Value)
- // {
- // Count++;
- // if(Count>=5) return New_Value;
- // Delay_us(1);
- // New_Value=Get_Io;
- // }
- // return Value;
- //}
-
- //int Trailing_Filter_Two(void) //滤波2+角度结算
- //{
- // int Speed_ori;
- // for(i=0;i<16;i++)
- // {
- // RAY[i]=1;
- // RAY_try[i]=1;
- // }
- // for(i=0;i<200;i++) //800
- // {
- // RAY_try[0]=Trailing_Filter_One(1,LED1);//滤波
- // RAY_try[1]=Trailing_Filter_One(1,LED2);
- // RAY_try[2]=Trailing_Filter_One(1,LED3);
- // RAY_try[3]=Trailing_Filter_One(1,LED4);
- // RAY_try[4]=Trailing_Filter_One(1,LED5);
- //
- // RAY[0]&=RAY_try[0];
- // RAY[1]&=RAY_try[1];
- // RAY[2]&=RAY_try[2];
- // RAY[3]&=RAY_try[3];
- // RAY[4]&=RAY_try[4];
- // delay_us(100); //增加稳定性
- // }
- // Speed_ori=Trailing_Speed();
- // return (Speed_ori);
- //}
Trailing.h
- #ifndef __TRAILING_H
- #define __TRAILING_H
-
- #include "sys.h"
-
- #define LED1 PAin(15)
- #define LED2 PBin(3)
- #define LED3 PBin(4)
- #define LED4 PBin(5)
- #define LED5 PBin(6)
-
- #define LED2_1(a) if(a) \
- GPIO_SetBits(TRAILING_PORT_2,IO4); \
- else GPIO_ResetBits(TRAILING_PORT_2,IO4);
-
-
- #define TRAILING_PORT_1 GPIOA
- #define TRAILING_PORT_2 GPIOB
- #define TRAILING_CLK_1 RCC_APB2Periph_GPIOA
- #define TRAILING_CLK_2 RCC_APB2Periph_GPIOB
-
- #define IO5 GPIO_Pin_15
- #define IO4 GPIO_Pin_3
- #define IO3 GPIO_Pin_4
- #define IO2 GPIO_Pin_5
- #define IO1 GPIO_Pin_6
-
- //起始速度值
- extern int Speed_Begin;
-
- //#define Ray[0] PAin(8)
- //#define Ray[1] PAin(9)
- //#define Ray[2] PAin(10)
- //#define Ray[3] PAin(11)
- //#define Ray[4] PAin(12)
-
- //#define LED1 PAin(8)
- //#define LED2 PAin(9)
- //#define LED3 PAin(10)
- //#define LED4 PAin(11)
- //#define LED5 PAin(12)
-
- //循迹模块初始化
- void Trailing_Init(void);
-
- //五路循迹模块加权函数
- int Trailing_Speed(void);
-
- //五路循迹模块情况汇总函数
- int Track_State(void);
-
- //u8 Trailing_Filter_One(u8 Value,u8 Get_Io);
- //int Trailing_Filter_Two(void);
-
- //运动轨迹调整函数
- void Trailing_Adjust(void);
-
- #endif
-
-
-
- //OLED_ShowString(1,1,"NUM:");
- //int NUM;
- //while(1)
- //{
- // Trailing_Speed();
- // Trailing_Adjust();
- // NUM = Track_State();
- // OLED_ShowNum(1,6,NUM,5);
- //}
-
-
-
2、跟踪模块代码 HCSR04.c
- #include "stm32f10x.h" // Device header
- #include "sys.h"
- #include "delay.h"
- #include "PID.h"
- #include "Motor.h"
- #include "HCSR04.h"
- #include "OLED.h"
-
- extern int Speed_Begin;
- int Location_Begin = 30;
-
- //超声波计数
- u16 msHcCount = 0;
-
-
- /*====================================
- 函数 :打开定时器函数
- 参数 :无
- 返回值 :无
- 描述 :通过使能TIMx
- ====================================*/
- static void HCSR04_TimerOpen()
- {
- TIM_SetCounter(HCSR04_TIM,0);
- msHcCount = 0;
- TIM_Cmd(HCSR04_TIM, ENABLE);
- }
-
- /*====================================
- 函数 :关闭定时器函数
- 参数 :无
- 返回值 :无
- 描述 :通过失能TIMx
- ====================================*/
- static void HCSR04_TimerClose()
- {
- TIM_Cmd(HCSR04_TIM, DISABLE);
- }
-
- //定时器中断
- void TIM3_IRQHandler(void)
- {
- if (TIM_GetITStatus(HCSR04_TIM, TIM_IT_Update) != RESET)
- {
- TIM_ClearITPendingBit(HCSR04_TIM, TIM_IT_Update);
- msHcCount++;
- }
- }
-
- /*====================================
- 函数 :获取定时器计数器值函数
- 参数 :无
- 返回值 :获取高电平时间
- 描述 :通过获取定时器计数器值,
- 通过计算得到高电平时间
- ====================================*/
- u32 HCSR04_GetEchoTimer()
- {
- u32 t = 0;
- t = msHcCount * 1000;
- t += TIM_GetCounter(HCSR04_TIM);
- HCSR04_TIM -> CNT = 0;
- delay_ms(50);
- // msHcCount = 0;
- return t;
- }
-
- /*====================================
- 函数 :通过定时器3计数器值推算距离函数
- 参数 :无
- 返回值 :超声波测距的距离
- 描述 :通过控制io口的开关与超声波模块的使用
- ,通过计算值得到最终模块的测距
- ====================================*/
- float Hcsr04_GetLength(void )
- {
- u32 t = 0;
- int i = 0;
- float lengthTemp = 0;
- float sum = 0;
- while(i!=2)
- {
- TRIG_Send = 1;
- delay_us(20);
- TRIG_Send = 0;
- while(ECHO_Reci == 0);
- HCSR04_TimerOpen();
- i = i + 1;
- while(ECHO_Reci == 1);
- HCSR04_TimerClose();
- t = HCSR04_GetEchoTimer();
- lengthTemp = ((float)t / 58.0);//cm
- sum = lengthTemp + sum ;
- }
- lengthTemp = sum/2.0;
- return lengthTemp;
- }
-
-
- /*====================================
- 函数 :位置调整函数
- 参数 :无
- 返回值 :无
- 描述 :先通得出现在的距离值(上限~下限),
- 然后进行PID计算得出PID距离值
- 如,距离值大于上限值,前进
- 距离值小于下限值,后退
- 距离值在上下限之间,停止
- ====================================*/
- void Location_Adjust(void)
- {
- int Speed;
- float Speed_1,Speed_2;
- Speed_1 = Location_PID(Hcsr04_GetLength());
- Speed_2 = (int)Speed_1;
- Speed = Speed_Begin + Speed_2;
- if (Location_Begin >= Hcsr04_GetLength()) Motor_Retreat(Speed - 2 * Speed_2);
- else if(Location_Begin + 5 < Hcsr04_GetLength()) Motor_Straight(Speed);
- }
HCSR04.h
- #ifndef __HCSR04_H
- #define __HCSR04_H
-
- #include "sys.h"
-
- //超声波硬件接口定义
- #define HCSR04_TIM TIM3
- #define HCSR04_TIMCLK RCC_APB1Periph_TIM3
-
- #define HCSR04_PORT GPIOA
- #define HCSR04_CLK RCC_APB2Periph_GPIOA
- #define HCSR04_TRIG GPIO_Pin_7 //输出
- #define HCSR04_ECHO GPIO_Pin_6 //输入
-
- #define ECHO_Reci PAin(6)
- #define TRIG_Send PAout(7)
-
- extern int Speed_Begin;
- extern int Location_Begin;
-
- //起始速度值
- extern int Speed_Begin;
- //起始距离值
- extern int Location_Begin;
-
- //配置超声波模块定时器
- void HCSR04_NVIC(void);
-
- //超声波模块初始化
- void HCSR04_Init(void);
-
- //打开定时器
- static void HCSR04_TimerOpen(void);
-
- //关闭定时器
- static void HCSR04_TimerClose(void);
-
- //获取定时器计数器值
- u32 HCSR04_GetEchoTimer(void);
-
- //推算距离
- float Hcsr04_GetLength(void );
-
- //位置调整
- void Location_Adjust(void);
-
-
- #endif
-
-
-
- // while(1)
- // {
- Motor_Retreat(50);
- // Location_Adjust();
- // delay_ms(10);
- // }
-
-
3,蜂鸣器音乐模块 Beep.c
- #include "stm32f10x.h" // Device header
- #include "beep.h"
-
-
- int melody[] = {50, 50, 50, 50, 200, 200, 200, 400, 400, 500, 500, 500};
-
- /*====================================
- 函数 :发声1基本函数
- 参数 :
- 返回值 :
- 描述 :
- ====================================*/
- void Sound(u16 frq)
- {
- u32 time;
- if(frq != 1000)
- {
- // time = 500000/((u32)frq);
- time = 100000/((u32)frq);
- PBeep = 1;
- delay_us(time);
- PBeep = 0;
- delay_us(time);
- }
- else
- delay_us(1000);
- }
-
- /*====================================
- 函数 :发声2基本函数
- 参数 :
- 返回值 :
- 描述 :
- ====================================*/
- void Sound2(u16 time)
- {
- PBeep = 1;
- delay_ms(time);
- PBeep = 0;
- delay_ms(time);
- }
-
- /*====================================
- 函数 :成功音效(先快后慢)
- 参数 :
- 返回值 :
- 描述 :得得得 得 得 得
- ====================================*/
- void play_successful(void)
- {
- int id=0;
- for(id = 0 ;id < 5 ;id++)
- {
- Sound2(melody[id]);
- }
- }
-
- /*====================================
- 函数 :失败音效(先快后慢)
- 参数 :
- 返回值 :
- 描述 :得 得 得 得得得
- ====================================*/
- void play_failed(void)
- {
- int id=0;
- for(id = 5 ;id >=0 ;id--)
- {
- Sound2(melody[id]);
- }
- }
-
-
- /*====================================
- 函数 :播放音乐
- 参数 :
- 返回值 :
- 描述 :音乐库中有:红尘情歌
- 小燕子
- (注意:music和time前面两版含
- 有13这个音,但由于不能正常发音,
- 后面的为删除这个音节的版本)
- ====================================*/
- void play_music(void)
- {
- // 低7 1 2 3 4 5 6 7 高1 高2 高3 高4 高5 不发音
- uc16 tone[] = {247,262,294,330,349,392,440,494,523,587,659,698,784,1000};//音频数据表
- // //红尘情歌
- // u8 music[]={5,5,6,8,7,6,5,6,13,13,//音调
- // 5,5,6,8,7,6,5,3,13,13,
- // 2,2,3,5,3,5,6,3,2,1,
- // 6,6,5,6,5,3,6,5,13,13,
-
- // 5,5,6,8,7,6,5,6,13,13,
- // 5,5,6,8,7,6,5,3,13,13,
- // 2,2,3,5,3,5,6,3,2,1,
- // 6,6,5,6,5,3,6,1,
-
- // 13,8,9,10,10,9,8,10,9,8,6,
- // 13,6,8,9,9,8,6,9,8,6,5,
- // 13,2,3,5,5,3,5,5,6,8,7,6,
- // 6,10,9,9,8,6,5,6,8};
- // u8 time[] = {2,4,2,2,2,2,2,8,4, 4, //时间
- // 2,4,2,2,2,2,2,8,4, 4,
- // 2,4,2,4,2,2,4,2,2,8,
- // 2,4,2,2,2,2,2,8,4 ,4,
-
- // 2,4,2,2,2,2,2,8,4, 4,
- // 2,4,2,2,2,2,2,8,4, 4,
- // 2,4,2,4,2,2,4,2,2,8,
- // 2,4,2,2,2,2,2,8,
-
- // 4, 2,2,2, 4, 2,2,2, 2,2,8,
- // 4, 2,2,2,4,2,2,2,2,2,8,
- // 4, 2,2,2,4,2,2,5,2,6,2,4,
- // 2,2 ,2,4,2,4,2,2,12};
-
- // //小燕子
- // u8 music[]={3,5,8,6,5,13,//音调
- // 3,5,6,8,5,13,
- // 8,10,9,8,9,8,6,8,5,13,
- // 3,5,6,5,6,8,9,5,6,13,
- // 3,2,1,2,13,
- // 2,2,3,5,5,8,2,3,5,13};
- // u8 time[] ={2,2,2,2,6,4,//时间
- // 2,2,2,2,6,4,
- // 6,2,4,4,2,2,2,2,6,4,
- // 6,2,4,2,2,4,2,2,6,4,
- // 2,2,4,6,4,
- // 4,2,2,4,4,4,2,2,6,4};
-
- //删去音节13的两首歌版本
- //红尘情歌
- u8 music[]={5,5,6,8,7,6,5,6,//音调
- 5,5,6,8,7,6,5,3,
- 2,2,3,5,3,5,6,3,2,1,
- 6,6,5,6,5,3,6,5,
-
- 5,5,6,8,7,6,5,6,
- 5,5,6,8,7,6,5,3,
- 2,2,3,5,3,5,6,3,2,1,
- 6,6,5,6,5,3,6,1,
-
- 8,9,10,10,9,8,10,9,8,6,
- 6,8,9,9,8,6,9,8,6,5,
- 2,3,5,5,3,5,5,6,8,7,6,
- 6,10,9,9,8,6,5,6,8};
- u8 time[] = {2,4,2,2,2,2,2,8, //时间
- 2,4,2,2,2,2,2,8,
- 2,4,2,4,2,2,4,2,2,8,
- 2,4,2,2,2,2,2,8,
-
- 2,4,2,2,2,2,2,8,
- 2,4,2,2,2,2,2,8,
- 2,4,2,4,2,2,4,2,2,8,
- 2,4,2,2,2,2,2,8,
-
- 2,2,2, 4, 2,2,2, 2,2,8,
- 2,2,2,4,2,2,2,2,2,8,
- 2,2,2,4,2,2,5,2,6,2,4,
- 2,2 ,2,4,2,4,2,2,12};
-
- // //小燕子
- // u8 music[]={3,5,8,6,5,//音调
- // 3,5,6,8,5,
- // 8,10,9,8,9,8,6,8,5,
- // 3,5,6,5,6,8,9,5,6,
- // 3,2,1,2,
- // 2,2,3,5,5,8,2,3,5};
- // u8 time[] ={2,2,2,2,6,//时间
- // 2,2,2,2,6,
- // 6,2,4,4,2,2,2,2,6,
- // 6,2,4,2,2,4,2,2,6,
- // 2,2,4,6,
- // 4,2,2,4,4,4,2,2,6};
-
- // u8 music[]={13,1,2,3,4,5,6,7,8};//测试基础音
- // u8 time[] ={4, 4,4,4,4,4,4,4,4};
-
- u32 yanshi;
- u16 i,e;
- yanshi = 2;//10 ; 4; 2;
- for(i=0;i<sizeof(music)/sizeof(music[0]);i++)
- {
- for(e=0;e<((u16)time[i])*tone[music[i]]/yanshi;e++)
- {
- Sound((u32)tone[music[i]]);
- }
- }
- GPIO_ResetBits(BEEP_PORT,BEEP_IO);
-
- }
-
- void play_music2(void)
- {
- // 低7 1 2 3 4 5 6 7 高1 高2 高3 高4 高5 不发音
- uc16 tone[] = {247,262,294,330,349,392,440,494,523,587,659,698,784,1000};//音频数据表
- //小燕子
- u8 music[]={3,5,8,6,5,//音调
- 3,5,6,8,5,
- 8,10,9,8,9,8,6,8,5,
- 3,5,6,5,6,8,9,5,6,
- 3,2,1,2,
- 2,2,3,5,5,8,2,3,5};
- u8 time[] ={2,2,2,2,6,//时间
- 2,2,2,2,6,
- 6,2,4,4,2,2,2,2,6,
- 6,2,4,2,2,4,2,2,6,
- 2,2,4,6,
- 4,2,2,4,4,4,2,2,6};
-
- u32 yanshi;
- u16 i,e;
- yanshi = 2;//10 ; 4; 2;
- for(i=0;i<sizeof(music)/sizeof(music[0]);i++)
- {
- for(e=0;e<((u16)time[i])*tone[music[i]]/yanshi;e++)
- {
- Sound((u32)tone[music[i]]);
- }
- }
- GPIO_ResetBits(BEEP_PORT,BEEP_IO);
- }
音乐模块是学习博客其他优秀博主的写法
Beep.h
- #ifndef __BEEP_H
- #define __BEEP_H
-
- #include "sys.h"
- #include "stdlib.h"
- #include "delay.h"
-
- #define BEEP_PORT GPIOA
- #define BEEP_CLK RCC_APB2Periph_GPIOA
- #define BEEP_IO GPIO_Pin_5 //输出
-
- //定义GPIOB的位地址变量宏,位输入宏,输出宏
- #define PBeep PAout(5)
-
- //Beep蜂鸣器初始化
- void Beep_Init(void);
-
- //发出声音1
- void Sound(u16 frq);
-
- //发出声音2
- void Sound2(u16 time);
-
- //放音乐
- void play_music(void);
-
- //放音乐2
- void play_music2(void);
-
-
- //播放成功
- void play_successful(void);
-
- //播放失败
- void play_failed(void);
-
- #endif
-
4.串口模块代码 Serial.c
这部分代码最多,即讲主函数操作都封装起来了,因为我们是蓝牙小车对吧
- #include "stm32f10x.h" // Device header
- #include "Serial.h"
-
- uint8_t Serial_RxPacket[4];
- uint8_t Serial_TxPacket[4];
-
- char Serial_RxPacket_W[100];
- uint8_t Serial_RxFlag;
- uint8_t RxData;
-
-
- int NUM;//A -> 循迹模式:代表此时遮挡情况
- float length;//B -> 跟踪模式:代表此时相对遮挡物的距离
-
- void Serial_Pattern(void)
- {
- // Serial_SendString("OPEN_OK\r\n");
- // printf("正在为您播放“程序开始”\r\n");
- OLED_ShowString(1,1,"OK1");
- if(Serial_RxFlag == 1)
- {
- OLED_Clear();//OLED清屏
- OLED_ShowString(1,1,"OK1");
- if (strcmp(Serial_RxPacket_W, "A") == 0)
- {
- //先停下我们的车
- Serial_Stop();
- //做好进入模式的准备
- Serial_Pattern_into(1);
- OLED_ShowString(1,1,"OK 12");
- Serial_SendString("Pattern_A_OK\r\n");
- //在没有新的指令到来一直保持循迹模式
- while(Serial_RxFlag == 0) Serial_PAT_Trailing();
- }
- else if (strcmp(Serial_RxPacket_W, "B") == 0)
- {
- Serial_Stop();
- Serial_Pattern_into(2);
- Serial_SendString("Pattern_B_OK\r\n");
- while(Serial_RxFlag == 0) Serial_PAT_Track();
- }
- else if (strcmp(Serial_RxPacket_W, "C") == 0)
- {
- Serial_Stop();
- // OLED_ShowString(1,1,"OK 10");
- Serial_Pattern_into(3);
- Serial_SendString("Pattern_C_OK\r\n");
- while(Serial_RxFlag == 0) Serial_PAT_Music();
- }
- else if (strcmp(Serial_RxPacket_W, "w") == 0)
- {
- Serial_RxFlag = 0;//清空蓝牙接收标志位
- Serial_Straight(60);
- }
- else if (strcmp(Serial_RxPacket_W, "s") == 0)
- {
- Serial_RxFlag = 0;//清空蓝牙接收标志位
- Serial_Retreat(60);
- }
- else if (strcmp(Serial_RxPacket_W, "a") == 0)
- {
- Serial_RxFlag = 0;//清空蓝牙接收标志位
- Serial_Left(60);
- }
- else if (strcmp(Serial_RxPacket_W, "d") == 0)
- {
- Serial_RxFlag = 0;//清空蓝牙接收标志位
- Serial_Right(60);
- }
- else
- {
- Serial_Stop();
- Serial_PAT_XX();
- }
-
- }
- }
-
-
- /*====================================
- 函数 :进入模式准备
- 参数 :无
- 返回值 :无
- 描述 :
- ====================================*/
- void Serial_Pattern_into(u8 x)
- {
- // int i;
-
- Serial_RxFlag = 0;//清空蓝牙接收标志位
-
- // LED_Pattern_1();//灯光效果一 -> 进入模式一循迹模式
- // LED_Pattern_1();
- // OLED_ShowString(1,1,"Trailing_Mode_OK");//显示正在做的功能
-
- Serial_LEDPx(x);
-
- OLED_ShowString(3,1,"LI:");
- OLED_ShowString(4,1,"TI:");
-
- // for(i = 0; i <= 0; i++)
- // {
- // Servo_SP();
- // delay_ms(5);
- // }
-
- // i = 0;
-
- play_successful();//成功效果音
- GPIO_SetBits(BEEP_PORT,BEEP_IO);//因为蜂鸣器不会自己关
- }
-
- /*====================================
- 函数 :进入模式的灯光与OLED准备
- 参数 :无
- 返回值 :无
- 描述 :
- ====================================*/
- void Serial_LEDPx(u8 x)
- {
- switch(x)
- {
- case 1: LED_Pattern_1(); LED_Pattern_1(); break;
- case 2: LED_Pattern_2(); LED_Pattern_2(); break;
- case 3: LED_Pattern_3(); LED_Pattern_3(); break;
- }
-
- switch(x)
- {
- case 1: OLED_ShowString(1,1,"Trailing_Mode_OK"); break;
- case 2: OLED_ShowString(1,1,"Track_Mode_OK"); break;
- case 3: OLED_ShowString(1,1,"Music_Mode_OK"); break;
- }
- }
-
-
- /*====================================
- 函数 :循迹模式操作(A)
- 参数 :无
- 返回值 :无
- 描述 :
- ====================================*/
- void Serial_PAT_Trailing(void)
- {
- Trailing_Adjust();
- NUM = Track_State();
-
- OLED_ShowString(2,1,"NUM:00000");
- OLED_ShowNum(2,6,NUM,5);
- Serial_PAT_TL();//每次操作的同时都显示一下此时的温度与光强
-
- // printf("遮挡情况:%dcm\n",NUM);
- }
- /*====================================
- 函数 :跟踪模式操作(B)
- 参数 :无
- 返回值 :无
- 描述 :
- ====================================*/
- void Serial_PAT_Track(void)
- {
- Location_Adjust();
- length = Hcsr04_GetLength();
-
- OLED_ShowString(2,1,"Lo:");
- OLED_ShowNum(2,4,length,3);
- Serial_PAT_TL();//每次操作的同时都显示一下此时的温度与光强
-
- // printf("距离:%.3fcm\n",length);
- }
- /*====================================
- 函数 :音乐模式操作(C)
- 参数 :无
- 返回值 :无
- 描述 :
- ====================================*/
- void Serial_PAT_Music(void)
- {
- int i;
- Serial_PAT_TL();//每次操作的同时都显示一下此时的温度与光强
- // printf("正在为您播放“红尘情歌”");
- OLED_ShowString(2,1,"open'hongchenqingge'");
- play_music();
-
- for(i = 0; i <= 0; i++)
- {
- Servo_SP();//舵机来回转动函数
- delay_ms(5);
- }
- i = 0;
- delay_ms(2);
- Serial_PAT_TL();//实时显示温度与光强
-
- Serial_PAT_TL();//每次操作的同时都显示一下此时的温度与光强
- // printf("正在为您播放“小燕子”");
- OLED_ShowString(2,1,"open'xiaoyanzi'");
- play_music2();
-
- for(i = 0; i <= 0; i++)//由于串口与定时器的尴尬关系,这里加一个循环可以使舵机转动更加稳定
- {
- Servo_SP();
- delay_ms(5);
- }
- i = 0;
- delay_ms(2);
- Serial_PAT_TL();//实时显示温度与光强
-
- }
-
- /*====================================
- 函数 :错误指令模式操作(D)
- 参数 :无
- 返回值 :无
- 描述 :
- ====================================*/
- void Serial_PAT_XX(void)
- {
- Serial_RxFlag = 0;//清空蓝牙接收标志位
- OLED_Clear();//OLED清屏
-
- Serial_SendString("ERROR_COMMAND\r\n");
- OLED_ShowString(1, 1, "ERROR_RX");
- OLED_ShowString(2, 1, "ERROR_COMMAND");
- OLED_ShowString(3,1,"LI:");
- OLED_ShowString(4,1,"TI:");
-
-
- play_failed();//失败效果音
- GPIO_SetBits(BEEP_PORT,BEEP_IO);//因为蜂鸣器不会自己关
- while(Serial_RxFlag == 0)
- {
- Servo_SP();//舵机来回摆动一次
- delay_ms(2);
- Serial_PAT_TL();//实时显示温度与光强
- }
- }
-
- /*====================================
- 函数 :判断此时温度与光强
- 参数 :无
- 返回值 :无
- 描述 :
- ====================================*/
- void Serial_PAT_TL(void)
- {
- if(ADValue[0] >= 2000)
- {
- // OLED_ShowString(3,4,"SO Light");
- OLED_ShowNum(3,4,ADValue[0],4);//发送数据包储存数组第一位数据
- }
- else
- {
- // OLED_ShowString(3,4,"Not Light");
- OLED_ShowNum(3,4,ADValue[0],4);
- }
-
- if(ADValue[1] >= 2100)
- {
- // OLED_ShowString(4,4,"SO hot");
- OLED_ShowNum(4,4,ADValue[1],4);//发送数据包储存数组第二位数据
- }
- else
- {
- // OLED_ShowString(4,4,"Not hot");
- OLED_ShowNum(4,4,ADValue[1],4);
- }
- }
-
- /*====================================
- 函数 :运动控制指令——前进,后退,左转,右转
- 参数 :无
- 返回值 :无
- 描述 :
- ====================================*/
- void Serial_Straight(u8 x)
- {
- while(Serial_RxFlag == 0)
- {
- Motor_Straight(x);
- }
- }
- void Serial_Retreat(u8 x)
- {
- while(Serial_RxFlag == 0)
- {
- Motor_Retreat(x);
- }
- }
- void Serial_Right(u8 x)
- {
- Motor_Right(x);
- delay_ms(1500);
-
- while(Serial_RxFlag == 0)
- {
- Motor_Straight(x);
- }
- }
- void Serial_Left(u8 x)
- {
- Motor_Left(x);
- delay_ms(1200);
- while(Serial_RxFlag == 0)
- {
- Motor_Straight(x);
- }
- }
- void Serial_Stop(void)
- {
- Motor_Stop();
- }
Serial.h
- #ifndef __SERIAL_H
- #define __SERIAL_H
-
- #include <stdio.h>
- #include <stdarg.h>
- #include "Key.h"
- #include "string.h"
-
- #include "OLED.h"
- #include "Motor.h"
- #include "Trailing.h"
- #include "PID.h"
- #include "HCSR04.h"
- #include "AD.h"
- #include "PWM.h"
- #include "led.h"
- #include "Beep.h"
- #include "Servo.h"
-
-
- extern uint8_t Serial_RxPacket[4];
- extern uint8_t Serial_TxPacket[4];
- extern char Serial_RxPacket_W[100];
- extern uint8_t Serial_RxFlag;//传输数据标志位
- extern uint8_t RxData;
-
- //发送HEX数据包函数
- void Serial_SendPacket(uint8_t *ABC,uint8_t Length);
-
- //清除标志位Serial_RxFlag
- uint8_t Serial_GetRxFlag(void);
-
- 封装数据函数(接收数据)
- //uint8_t Serial_GetRxData(void);
-
- //串口中断
- //void USART1_IRQHandler(void);
- void USART1_IRQHandler(void);
-
- //串口初始化
- void Serial_Init(void);
-
- //串口发送函数
- void Serial_SendByte(uint8_t Byte);//一个字节
- void Serial_SendArray(uint8_t *Array, uint16_t Length);//一个数组
- void Serial_SendString(char *String);//一串字符串(文本)
- uint32_t Serial_Pow(uint32_t X,uint32_t Y);//次方函数(与发送数函数结合使用)
- void Serial_SendNumber(uint32_t Number,uint8_t Length);//一个十六进制数
-
- //printf的底层函数
- int fputc(int ch, FILE *f);
-
- //sprintf封装函数
- void Serial_Printf(char *format, ...);
-
-
- /*====================================
- printf打印函数经常乱码解决方案
- 1-UTF8不乱码方案
- 工程选项(魔术棒) -> C/C++ -> Misc Controles ->填上: --no-multibyte-chars
- 2-GB2312编码
- 设置 -> Encoding -> 删掉汉字 -> 关闭小文件再打开 -> 再编辑文字 -> 串口选择GBK编码
- ====================================*/
-
-
-
- extern int NUM;//A -> 循迹模式:代表此时遮挡情况
- extern float length;//B -> 跟踪模式:代表此时相对遮挡物的距离
-
- //模式选择
- void Serial_Pattern(void);
-
- //进入模式准备
- void Serial_Pattern_into(u8 x);
-
- //准备灯光与OLED模式
- void Serial_LEDPx(u8 x);
-
- //判断此时温度与光强
- void Serial_PAT_TL(void);
-
- //循迹模式操作——A B C D
- void Serial_PAT_Trailing(void);//A-循迹模式
- void Serial_PAT_Track(void);//B-跟踪模式
- void Serial_PAT_Music(void);//C-音乐模式
- void Serial_PAT_XX(void);//D-错误指令模式
-
- //运动控制指令——前进,后退,左转,右转
- void Serial_Straight(u8 x);//前进
- void Serial_Retreat(u8 x);//后退
- void Serial_Right(u8 x);//右转
- void Serial_Left(u8 x);//左转
- void Serial_Stop(void);//停下
-
- #endif
5,PID模块代码 PID.c
- #include "stm32f10x.h" // Device header
- #include "PID.h"
- #include "Trailing.h"
- #include "Motor.h"
- #include "Delay.h"
- #include "HCSR04.h"
- #include "usart.h"
- #include "OLED.h"
-
- //结构体声明
- PIDPara Location;
- PIDPara Speed;
-
- extern int Speed_Begin;
- extern int Location_Begin;
-
- /*====================================
- 函数 :循迹PID函数(位置式)
- 参数 :误差
- 返回值 :经过PID计算后的量
- 描述 :通过pid计算误差,将其加到原有的
- 计算中,得到需要的新速度量
- ====================================*/
- float Position_KP = 1.8;//例:速度80 -> 1.8 大概
- // Position_KI = 0.1,
- // Position_KD = 0;
-
- int Position_PID(int target)
- {
- static float Pwm ,
- Bias ;
- // Interqral_Bias ;
- // Last_Bias;
-
- Bias = target;
- // Interqral_Bias += Bias;
- //
- // if(Interqral_Bias > + 6) Interqral_Bias = + 6;
- // if(Interqral_Bias < - 6) Interqral_Bias = - 6;
-
- Pwm = Position_KP * Bias ;
- // Position_KI * Interqral_Bias ;
- // Position_KD * (Bias - Last_Bias);
-
- // Last_Bias = Bias;
-
- return Pwm;
- }
-
-
-
- /*====================================
- 函数 :跟踪PID函数(位置环+速度环)
- 参数 :误差
- 返回值 :经过PID计算后的量
- 描述 :通过pid计算误差,将其加到原有的
- 计算中,得到需要的新速度量
- ====================================*/
- void PID_Init(void)
- {
- //pid 参数初始化
- Location.Kp = 1;
- Location.Ki = 0;
- Location.Kd = 0;
-
- Location.C0 = 0;
- Location.C1 = 0;
- Location.Cmin = 0;
- Location.Cmax = 0;
-
- Location.E0 = 0;
- Location.E1 = 0;
- Location.E2 = 0;
-
- Speed.Kp = 1.5;
- Speed.Ki = 0;
- Speed.Kd = 0;
-
- Speed.C0 = 0;
- Speed.C1 = 0;
- Speed.Cmin = 0;
- Speed.Cmax = 0;
-
- Speed.E0 = 0;
- Speed.E1 = 0;
- Speed.E2 = 0;
- }
-
- /*====================================
- 函数 :跟踪PID函数(位置式)
- 参数 :误差
- 返回值 :经过PID计算后的量
- 描述 :通过pid计算误差,将其加到原有的
- 计算中,得到需要的新速度量
- ====================================*/
- int Location_PID(int target)
- {
- static float Pwm ,
- Bias;
- // Last_Bias;
-
- Bias = target - Location_Begin;
- Pwm = 1 * Bias ;
- // 0.3 * (Bias - Last_Bias);
-
- // Last_Bias = Bias;
-
- return Pwm;
- }
-
-
- //int Speed_PID(void)
- //{
- //
- // static float Speed_Pwm ,
- // Bias ,
- // Last_Bias;
- //
- // Bias = Location_PID(Hcsr04_GetLength());
-
- // Speed_Pwm = Speed.Kp * Bias +
- // Speed.Kd * (Bias - Last_Bias);
- //
- // Last_Bias = Bias;
- //
- // return Speed_Pwm;
- //}
PID.h
- #ifndef __PID_H
- #define __PID_H
-
- #include "sys.h"
-
- extern int Speed_Begin;
- extern int Location_Begin;
-
- int Position_PID(int target);
-
- //结构体
- typedef struct
- {
- //PID 参数系数K
- float Kp;
- float Ki;
- float Kd;
-
- //PID 控制量C:当前: C0 上一次:C1 范围控制量:Cmin Cmax
- float C0;
- float C1;
- float C2;
- float Cmin;
- float Cmax;
-
- // //PID 变量:当前: C0 上一次:C1 范围控制量:Cmin Cmax
- // static float Pwm;
- // static float Bias ;
- // static float Interqral_Bias;
- // static float Last_Bias;
-
- //PID 误差量E 当前: E0 上一次: E1 上上一次: E2
- float E0;
- float E1;
- float E2;
-
- }PIDPara; //PID 参数
-
- //PID初始化
- void PID_Init(void);
-
- //循迹模式PID函数
- int Speed_PID(void);
-
- //跟踪模式PID函数
- int Location_PID(int target);
-
- #endif
-
其他还有AD+DMA转运代码,大家在下载中查看吧
五、制作小车需要注意的地方
1,要提早分配好io口,这次制作的过程中就出现了使用PB3的尴尬情况,还要增加释放函数
2,串口与定时器互相影响,我当时原测试模块时初始化互相影响,但是后面将它们初始化放在上下靠近,看教程是定时器见初始化再初始化串口,一次解决,不过在使用串口的同时使用舵机容易造成舵机的抖动,这边偶然增加一个for循环以及delay可以解决这个影响问题,还算是运气很好哈哈
下面是实现的效果展示,我把它做成视频发布在b站:
STM32超级蓝牙小车——基于STM32F103C8T6的多功能蓝牙小车(PID循迹、跟踪、有源蜂鸣器播放音乐、蓝牙遥控、AD采集+DMA转运等超多元素小车)
六、代码源文件
链接:https://pan.baidu.com/s/1WZF10LWIbbE5Mubth__VEQ
提取码:grcc
这次的程序写了蛮多注释的,也是花了三天时间做的,感觉最大的问题就是串口与定时器关系的问题,程序上可能还存在错误,希望大家多多指正!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。